Merge branch 'charlesmchen/profiles7'

pull/1/head
Matthew Chen 8 years ago
commit e74e7f7cc9

@ -77,6 +77,8 @@
34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */; };
34CCAF3B1F0C2748004084F4 /* OWSAddToContactViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */; };
34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88E61F2FB9A10098030F /* ProfileViewController.m */; };
34CE88EC1F3237260098030F /* OWSProfileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88EA1F3237260098030F /* OWSProfileManager.m */; };
34CE88ED1F3237260098030F /* ProfileFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88EB1F3237260098030F /* ProfileFetcherJob.swift */; };
34D5CC961EA6AFAD005515DB /* OWSContactsSyncing.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */; };
34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */; };
34D5CCB11EAE7E7F005515DB /* SelectRecipientViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCB01EAE7E7F005515DB /* SelectRecipientViewController.m */; };
@ -168,7 +170,6 @@
45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; };
45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; };
4585C4601ED4FD0400896AEA /* OWS104CreateRecipientIdentities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4585C45F1ED4FD0400896AEA /* OWS104CreateRecipientIdentities.m */; };
4585C4661ED5DF7A00896AEA /* ProfileFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4585C4651ED5DF7A00896AEA /* ProfileFetcherJob.swift */; };
4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */; };
458967111DC117CC00E9DD21 /* AccountManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458967101DC117CC00E9DD21 /* AccountManagerTest.swift */; };
458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458DE9D51DEE3FD00071BB03 /* PeerConnectionClient.swift */; };
@ -506,6 +507,9 @@
34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAddToContactViewController.m; sourceTree = "<group>"; };
34CE88E51F2FB9A10098030F /* ProfileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileViewController.h; sourceTree = "<group>"; };
34CE88E61F2FB9A10098030F /* ProfileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProfileViewController.m; sourceTree = "<group>"; };
34CE88E91F3237260098030F /* OWSProfileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProfileManager.h; sourceTree = "<group>"; };
34CE88EA1F3237260098030F /* OWSProfileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProfileManager.m; sourceTree = "<group>"; };
34CE88EB1F3237260098030F /* ProfileFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileFetcherJob.swift; sourceTree = "<group>"; };
34D5CC941EA6AFAD005515DB /* OWSContactsSyncing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSyncing.h; sourceTree = "<group>"; };
34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSyncing.m; sourceTree = "<group>"; };
34D5CC981EA6EB79005515DB /* OWSMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageCollectionViewCell.h; sourceTree = "<group>"; };
@ -1074,6 +1078,16 @@
path = TSMessageAdapters;
sourceTree = "<group>";
};
34CE88E81F3237260098030F /* Profiles */ = {
isa = PBXGroup;
children = (
34CE88E91F3237260098030F /* OWSProfileManager.h */,
34CE88EA1F3237260098030F /* OWSProfileManager.m */,
34CE88EB1F3237260098030F /* ProfileFetcherJob.swift */,
);
path = Profiles;
sourceTree = "<group>";
};
34D8C0221ED3673300188D7C /* DebugUI */ = {
isa = PBXGroup;
children = (
@ -1274,21 +1288,22 @@
76EB03C118170B33006006FC /* src */ = {
isa = PBXGroup;
children = (
45CD81A41DBFF8CF004C9430 /* Storyboards */,
76EB03C218170B33006006FC /* AppDelegate.h */,
76EB03C318170B33006006FC /* AppDelegate.m */,
76EB03FE18170B33006006FC /* call */,
76EB040318170B33006006FC /* contact */,
70DBA29918CFE98500771DAD /* crypto */,
76EB041118170B33006006FC /* environment */,
76EB041D18170B33006006FC /* network */,
B60959791C2C0FA9004E8797 /* rating */,
76EB04C818170B33006006FC /* util */,
45D231751DC7E8C50034FA89 /* Jobs */,
457F3AC01D14A0F700C51351 /* Models */,
450DF2061E0DD28D003D14BE /* UserInterface */,
76EB041D18170B33006006FC /* network */,
45BFFFA51D898AB8004A12A7 /* Observers */,
34CE88E81F3237260098030F /* Profiles */,
B60959791C2C0FA9004E8797 /* rating */,
45B201741DAECBFD00C461E0 /* Signal-Bridging-Header.h */,
45CD81A41DBFF8CF004C9430 /* Storyboards */,
450DF2061E0DD28D003D14BE /* UserInterface */,
76EB04C818170B33006006FC /* util */,
);
path = src;
sourceTree = "<group>";
@ -2123,7 +2138,6 @@
buildActionMask = 2147483647;
files = (
34B3F8761E8DF1700035BE1A /* CodeVerificationViewController.m in Sources */,
4585C4661ED5DF7A00896AEA /* ProfileFetcherJob.swift in Sources */,
76EB063E18170B33006006FC /* Operation.m in Sources */,
34B3F8741E8DF1700035BE1A /* AttachmentSharing.m in Sources */,
34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */,
@ -2139,10 +2153,12 @@
3456710A1E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m in Sources */,
450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */,
340CB2241EAC155C0001CAA1 /* ContactsViewHelper.m in Sources */,
34CE88EC1F3237260098030F /* OWSProfileManager.m in Sources */,
45CD81F21DC03A22004C9430 /* OWSLogger.m in Sources */,
4542F0941EB9372700C7EE92 /* SystemContactsFetcher.swift in Sources */,
B60C16651988999D00E97A6C /* VersionMigrations.m in Sources */,
B97940271832BD2400BD66CB /* UIUtil.m in Sources */,
34CE88ED1F3237260098030F /* ProfileFetcherJob.swift in Sources */,
34B3F8791E8DF1700035BE1A /* CountryCodeViewController.m in Sources */,
4CE0E3771B954546007210CF /* TSAnimatedAdapter.m in Sources */,
4531C9C41DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m in Sources */,

@ -11,6 +11,7 @@
#import "NotificationsManager.h"
#import "OWSContactsManager.h"
#import "OWSContactsSyncing.h"
#import "OWSProfileManager.h"
#import "OWSStaleNotificationObserver.h"
#import "Pastelog.h"
#import "PropertyListPreferences.h"
@ -28,7 +29,6 @@
#import <SignalServiceKit/OWSFailedMessagesJob.h>
#import <SignalServiceKit/OWSIncomingMessageReadObserver.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSProfilesManager.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSDatabaseView.h>
#import <SignalServiceKit/TSMessagesManager.h>
@ -161,7 +161,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
DDLogInfo(@"%@ application: didFinishLaunchingWithOptions completed.", self.tag);
[OWSAnalytics appLaunchDidBegin];
[OWSProfilesManager.sharedManager appLaunchDidBegin];
return YES;
}
@ -236,7 +235,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[[TextSecureKitEnv alloc] initWithCallMessageHandler:[Environment getCurrent].callMessageHandler
contactsManager:[Environment getCurrent].contactsManager
messageSender:[Environment getCurrent].messageSender
notificationsManager:[Environment getCurrent].notificationsManager];
notificationsManager:[Environment getCurrent].notificationsManager
profileManager:OWSProfileManager.sharedManager];
[TextSecureKitEnv setSharedEnv:sharedEnv];
[[TSStorageManager sharedManager] setupDatabaseWithSafeBlockingMigrations:^{

@ -2,6 +2,8 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <SignalServiceKit/ProfileManagerProtocol.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kNSNotificationName_LocalProfileDidChange;
@ -10,7 +12,7 @@ extern NSString *const kNSNotificationName_OtherUsersProfileDidChange;
@class TSThread;
// This class can be safely accessed and used from any thread.
@interface OWSProfilesManager : NSObject
@interface OWSProfileManager : NSObject <ProfileManagerProtocol>
- (instancetype)init NS_UNAVAILABLE;
@ -18,11 +20,10 @@ extern NSString *const kNSNotificationName_OtherUsersProfileDidChange;
#pragma mark - Local Profile
@property (atomic, readonly) NSData *localProfileKey;
// These two methods should only be called from the main thread.
- (NSString *)localProfileName;
- (UIImage *)localProfileAvatarImage;
- (NSData *)localProfileKey;
- (nullable NSString *)localProfileName;
- (nullable UIImage *)localProfileAvatarImage;
// This method is used to update the "local profile" state on the client
// and the service. Client state is only updated if service state is
@ -34,9 +35,6 @@ extern NSString *const kNSNotificationName_OtherUsersProfileDidChange;
success:(void (^)())successBlock
failure:(void (^)())failureBlock;
// This method should only be called from the main thread.
- (void)appLaunchDidBegin;
#pragma mark - Profile Whitelist
- (void)addUserToProfileWhitelist:(NSString *)recipientId;
@ -59,7 +57,12 @@ extern NSString *const kNSNotificationName_OtherUsersProfileDidChange;
- (nullable UIImage *)profileAvatarForRecipientId:(NSString *)recipientId;
- (void)fetchProfileForRecipientId:(NSString *)recipientId;
- (void)refreshProfileForRecipientId:(NSString *)recipientId;
- (void)updateProfileForRecipientId:(NSString *)recipientId
profileNameEncrypted:(NSData *_Nullable)profileNameEncrypted
avatarUrlEncrypted:(NSData *_Nullable)avatarUrlEncrypted
avatarDigestEncrypted:(NSData *_Nullable)avatarDigestEncrypted;
@end

@ -2,34 +2,43 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSProfilesManager.h"
#import "NSData+hexString.h"
#import "NSDate+OWS.h"
#import "OWSMessageSender.h"
#import "SecurityUtils.h"
#import "TSAccountManager.h"
#import "TSGroupThread.h"
#import "TSStorageManager.h"
#import "TSThread.h"
#import "TSYapDatabaseObject.h"
#import "TextSecureKitEnv.h"
#import "OWSProfileManager.h"
#import "Environment.h"
#import "Signal-Swift.h"
#import <SignalServiceKit/NSData+hexString.h>
#import <SignalServiceKit/NSDate+OWS.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/SecurityUtils.h>
#import <SignalServiceKit/TSGroupThread.h>
#import <SignalServiceKit/TSStorageManager.h>
#import <SignalServiceKit/TSStorageManager.h>
#import <SignalServiceKit/TSThread.h>
#import <SignalServiceKit/TSYapDatabaseObject.h>
#import <SignalServiceKit/TextSecureKitEnv.h>
NS_ASSUME_NONNULL_BEGIN
// UserProfile properties should only be mutated on the main thread.
@interface UserProfile : TSYapDatabaseObject
@property (nonatomic, readonly) NSString *recipientId;
// These properties may be accessed from any thread.
@property (atomic, readonly) NSString *recipientId;
@property (atomic, nullable) NSData *profileKey;
// These properties may be accessed only from the main thread.
@property (nonatomic, nullable) NSString *profileName;
@property (nonatomic, nullable) NSString *avatarUrl;
@property (nonatomic, nullable) NSString *avatarDigest;
@property (nonatomic, nullable) NSData *avatarDigest;
// This filename is relative to OWSProfilesManager.profileAvatarsDirPath.
// This filename is relative to OWSProfileManager.profileAvatarsDirPath.
@property (nonatomic, nullable) NSString *avatarFileName;
// This should reflect when either:
//
// * The last successful update was started.
// * The in-flight update was started.
// * The last successful update finished.
// * The current in-flight update began.
//
// This property may be accessed from any thread.
@property (nonatomic, nullable) NSDate *lastUpdateDate;
- (instancetype)init NS_UNAVAILABLE;
@ -41,10 +50,6 @@ NS_ASSUME_NONNULL_BEGIN
@implementation UserProfile
- (instancetype)initWithRecipientId:(NSString *)recipientId
profileName:(NSString *_Nullable)profileName
avatarUrl:(NSString *_Nullable)avatarUrl
avatarDigest:(NSString *_Nullable)avatarDigest
avatarFileName:(NSString *_Nullable)avatarFileName
{
self = [super initWithUniqueId:recipientId];
@ -54,10 +59,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(recipientId.length > 0);
_recipientId = recipientId;
_profileName = profileName;
_avatarUrl = avatarUrl;
_avatarDigest = avatarDigest;
_avatarFileName = avatarFileName;
return self;
}
@ -68,8 +69,7 @@ NS_ASSUME_NONNULL_BEGIN
{
return ([other isKindOfClass:[UserProfile class]] && [self.recipientId isEqualToString:other.recipientId] &&
[self.profileName isEqualToString:other.profileName] && [self.avatarUrl isEqualToString:other.avatarUrl] &&
[self.avatarDigest isEqualToString:other.avatarDigest] &&
[self.avatarFileName isEqualToString:other.avatarFileName]);
[self.avatarDigest isEqual:other.avatarDigest] && [self.avatarFileName isEqualToString:other.avatarFileName]);
}
- (NSUInteger)hash
@ -82,31 +82,24 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark -
NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
NSString *const kNSNotificationName_LocalProfileDidChange = @"kNSNotificationName_LocalProfileDidChange";
NSString *const kNSNotificationName_OtherUsersProfileDidChange = @"kNSNotificationName_OtherUsersProfileDidChange";
NSString *const kOWSProfilesManager_Collection = @"kOWSProfilesManager_Collection";
// This key is used to persist the local user's profile key.
NSString *const kOWSProfilesManager_LocalProfileSecretKey = @"kOWSProfilesManager_LocalProfileSecretKey";
NSString *const kOWSProfilesManager_UserWhitelistCollection = @"kOWSProfilesManager_UserWhitelistCollection";
NSString *const kOWSProfilesManager_GroupWhitelistCollection = @"kOWSProfilesManager_GroupWhitelistCollection";
NSString *const kOWSProfilesManager_OtherUsersProfileKeysCollection
= @"kOWSProfilesManager_OtherUsersProfileKeysCollection";
NSString *const kOWSProfileManager_UserWhitelistCollection = @"kOWSProfileManager_UserWhitelistCollection";
NSString *const kOWSProfileManager_GroupWhitelistCollection = @"kOWSProfileManager_GroupWhitelistCollection";
// TODO:
static const NSInteger kProfileKeyLength = 16;
@interface OWSProfilesManager ()
@interface OWSProfileManager ()
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) TSNetworkManager *networkManager;
// This property should only be mutated on the main thread,
//
// NOTE: Do not access this property directly; use getOrCreateLocalUserProfile instead.
@property (nonatomic, nullable) UserProfile *localUserProfile;
@property (atomic, nullable) UserProfile *localUserProfile;
// This property should only be mutated on the main thread,
@property (nonatomic, nullable) UIImage *localCachedAvatarImage;
@ -115,7 +108,6 @@ static const NSInteger kProfileKeyLength = 16;
// These three properties can be accessed on any thread.
@property (atomic, readonly) NSMutableDictionary<NSString *, NSNumber *> *userProfileWhitelistCache;
@property (atomic, readonly) NSMutableDictionary<NSString *, NSNumber *> *groupProfileWhitelistCache;
@property (atomic, readonly) NSMutableDictionary<NSString *, NSData *> *otherUsersProfileKeyCache;
// This property should only be mutated on the main thread,
@property (nonatomic, readonly) NSCache<NSString *, UIImage *> *otherUsersProfileAvatarImageCache;
@ -124,11 +116,11 @@ static const NSInteger kProfileKeyLength = 16;
#pragma mark -
@implementation OWSProfilesManager
@implementation OWSProfileManager
+ (instancetype)sharedManager
{
static OWSProfilesManager *sharedMyManager = nil;
static OWSProfileManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initDefault];
@ -139,13 +131,15 @@ static const NSInteger kProfileKeyLength = 16;
- (instancetype)initDefault
{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
OWSMessageSender *messageSender = [Environment getCurrent].messageSender;
TSNetworkManager *networkManager = [Environment getCurrent].networkManager;
return [self initWithStorageManager:storageManager messageSender:messageSender];
return [self initWithStorageManager:storageManager messageSender:messageSender networkManager:networkManager];
}
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
messageSender:(OWSMessageSender *)messageSender
networkManager:(TSNetworkManager *)networkManager
{
self = [super init];
@ -156,32 +150,30 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert([NSThread isMainThread]);
OWSAssert(storageManager);
OWSAssert(messageSender);
OWSAssert(messageSender);
_messageSender = messageSender;
_dbConnection = storageManager.newDatabaseConnection;
_networkManager = networkManager;
_userProfileWhitelistCache = [NSMutableDictionary new];
_groupProfileWhitelistCache = [NSMutableDictionary new];
_otherUsersProfileKeyCache = [NSMutableDictionary new];
_otherUsersProfileAvatarImageCache = [NSCache new];
OWSSingletonAssert();
// Register this manager with the message sender.
// This is a circular dependency.
[messageSender setProfilesManager:self];
// Try to load.
_localProfileKey = [self.dbConnection objectForKey:kOWSProfilesManager_LocalProfileSecretKey
inCollection:kOWSProfilesManager_Collection];
if (!_localProfileKey) {
// Generate
_localProfileKey = [OWSProfilesManager generateLocalProfileKey];
// Persist
[self.dbConnection setObject:_localProfileKey
forKey:kOWSProfilesManager_LocalProfileSecretKey
inCollection:kOWSProfilesManager_Collection];
self.localUserProfile = [self getOrBuildUserProfileForRecipientId:kLocalProfileUniqueId];
OWSAssert(self.localUserProfile);
if (!self.localUserProfile.profileKey) {
self.localUserProfile.profileKey = [OWSProfileManager generateLocalProfileKey];
// Make sure to save on the local db connection for consistency.
//
// NOTE: we do an async read/write here to avoid blocking during app launch path.
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self.localUserProfile saveWithTransaction:transaction];
}];
}
OWSAssert(_localProfileKey.length == kProfileKeyLength);
OWSAssert(self.localUserProfile.profileKey.length == kProfileKeyLength);
return self;
}
@ -199,57 +191,55 @@ static const NSInteger kProfileKeyLength = 16;
object:nil];
}
- (void)appLaunchDidBegin
{
// Do nothing; we only want to make sure this singleton is created on startup.
}
#pragma mark - User Profile Accessor
- (UserProfile *)getOrCreateUserProfileForRecipientId:(NSString *)recipientId
// This method can be safely called from any thread.
- (UserProfile *)getOrBuildUserProfileForRecipientId:(NSString *)recipientId
{
OWSAssert([NSThread isMainThread]);
OWSAssert(recipientId.length > 0);
__block UserProfile *instance;
// Make sure to read on the local db connection for consistency.
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
instance = [UserProfile fetchObjectWithUniqueID:recipientId transaction:transaction];
if (!instance) {
instance = [[UserProfile alloc] initWithRecipientId:recipientId
profileName:nil
avatarUrl:nil
avatarDigest:nil
avatarFileName:nil];
[instance saveWithTransaction:transaction];
}
}];
if (!instance) {
instance = [[UserProfile alloc] initWithRecipientId:recipientId];
}
OWSAssert(instance);
return instance;
}
- (nullable UserProfile *)getOrCreateLocalUserProfile
// All writes to user profiles should occur on the main thread.
- (void)saveUserProfile:(UserProfile *)userProfile
{
OWSAssert([NSThread isMainThread]);
OWSAssert(userProfile);
if (!self.localUserProfile) {
NSString *_Nullable recipientId = [TSAccountManager localNumber];
if (!recipientId) {
OWSFail(@"Missing local number.");
return nil;
}
self.localUserProfile = [self getOrCreateUserProfileForRecipientId:recipientId];
// Make sure to save on the local db connection for consistency.
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[userProfile saveWithTransaction:transaction];
}];
if (userProfile == self.localUserProfile) {
[[NSNotificationCenter defaultCenter] postNotificationName:kNSNotificationName_LocalProfileDidChange
object:nil
userInfo:nil];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kNSNotificationName_OtherUsersProfileDidChange
object:nil
userInfo:nil];
}
return self.localUserProfile;
}
#pragma mark - Local Profile Key
+ (NSData *)generateLocalProfileKey
{
DDLogInfo(@"%@ Generating profile key for local user.", self.tag);
// TODO:
DDLogVerbose(@"%@ Profile key generation is not yet implemented.", self.tag);
return [SecurityUtils generateRandomBytes:kProfileKeyLength];
@ -257,14 +247,21 @@ static const NSInteger kProfileKeyLength = 16;
#pragma mark - Local Profile
- (NSString *)localProfileName
- (NSData *)localProfileKey
{
OWSAssert(self.localUserProfile.profileKey.length == kProfileKeyLength);
return self.localUserProfile.profileKey;
}
- (nullable NSString *)localProfileName
{
OWSAssert([NSThread isMainThread]);
return [self getOrCreateLocalUserProfile].profileName;
return self.localUserProfile.profileName;
}
- (UIImage *)localProfileAvatarImage
- (nullable UIImage *)localProfileAvatarImage
{
OWSAssert([NSThread isMainThread]);
@ -291,31 +288,26 @@ static const NSInteger kProfileKeyLength = 16;
//
// * Try to update the service.
// * Update client state on success.
void (^tryToUpdateService)(NSString *_Nullable, NSString *_Nullable, NSString *_Nullable) = ^(
NSString *_Nullable avatarUrl, NSString *_Nullable avatarDigest, NSString *_Nullable avatarFileName) {
void (^tryToUpdateService)(NSString *_Nullable, NSData *_Nullable, NSString *_Nullable) = ^(
NSString *_Nullable avatarUrl, NSData *_Nullable avatarDigest, NSString *_Nullable avatarFileName) {
[self updateProfileOnService:profileName
avatarUrl:avatarUrl
avatarDigest:avatarDigest
success:^{
// All reads and writes to user profiles should happen on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
UserProfile *userProfile = [self getOrCreateLocalUserProfile];
UserProfile *userProfile = self.localUserProfile;
OWSAssert(userProfile);
userProfile.profileName = profileName;
userProfile.avatarUrl = avatarUrl;
userProfile.avatarDigest = avatarDigest;
userProfile.avatarFileName = avatarFileName;
// Make sure to save on the local db connection for consistency.
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[userProfile saveWithTransaction:transaction];
}];
[self saveUserProfile:userProfile];
self.localCachedAvatarImage = avatarImage;
successBlock();
[[NSNotificationCenter defaultCenter] postNotificationName:kNSNotificationName_LocalProfileDidChange
object:nil
userInfo:nil];
});
}
failure:^{
@ -323,7 +315,7 @@ static const NSInteger kProfileKeyLength = 16;
}];
};
UserProfile *userProfile = [self getOrCreateLocalUserProfile];
UserProfile *userProfile = self.localUserProfile;
OWSAssert(userProfile);
// If we have a new avatar image, we must first:
@ -346,7 +338,7 @@ static const NSInteger kProfileKeyLength = 16;
success:^(NSData *data, NSString *fileName) {
[self uploadAvatarToService:data
fileName:fileName
success:^(NSString *avatarUrl, NSString *avatarDigest) {
success:^(NSString *avatarUrl, NSData *avatarDigest) {
tryToUpdateService(avatarUrl, avatarDigest, fileName);
}
failure:^{
@ -393,7 +385,7 @@ static const NSInteger kProfileKeyLength = 16;
// TODO: The exact API & encryption scheme for avatars is not yet settled.
- (void)uploadAvatarToService:(NSData *)data
fileName:(NSString *)fileName
success:(void (^)(NSString *avatarUrl, NSString *avatarDigest))successBlock
success:(void (^)(NSString *avatarUrl, NSData *avatarDigest))successBlock
failure:(void (^)())failureBlock
{
OWSAssert(data.length > 0);
@ -404,7 +396,7 @@ static const NSInteger kProfileKeyLength = 16;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// TODO:
NSString *avatarUrl = @"avatarUrl";
NSString *avatarDigest = @"avatarDigest";
NSData *avatarDigest = [@"avatarDigest" dataUsingEncoding:NSUTF8StringEncoding];
if (YES) {
successBlock(avatarUrl, avatarDigest);
return;
@ -416,7 +408,7 @@ static const NSInteger kProfileKeyLength = 16;
// TODO: The exact API & encryption scheme for profiles is not yet settled.
- (void)updateProfileOnService:(nullable NSString *)localProfileName
avatarUrl:(nullable NSString *)avatarUrl
avatarDigest:(nullable NSString *)avatarDigest
avatarDigest:(nullable NSData *)avatarDigest
success:(void (^)())successBlock
failure:(void (^)())failureBlock
{
@ -424,6 +416,12 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert(failureBlock);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// TODO: Do we need to use NSDataBase64EncodingOptions?
NSString *_Nullable localProfileNameEncrypted =
[[self encryptProfileString:localProfileName] base64EncodedString];
NSString *_Nullable avatarUrlEncrypted = [[self encryptProfileString:avatarUrl] base64EncodedString];
NSString *_Nullable avatarDigestEncrypted = [[self encryptProfileData:avatarDigest] base64EncodedString];
// TODO:
if (YES) {
successBlock();
@ -440,7 +438,7 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert([NSThread isMainThread]);
OWSAssert(recipientId.length > 0);
[self.dbConnection setBool:YES forKey:recipientId inCollection:kOWSProfilesManager_UserWhitelistCollection];
[self.dbConnection setBool:YES forKey:recipientId inCollection:kOWSProfileManager_UserWhitelistCollection];
self.userProfileWhitelistCache[recipientId] = @(YES);
}
@ -462,7 +460,7 @@ static const NSInteger kProfileKeyLength = 16;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *recipientId in recipientIds) {
[transaction setObject:@(YES) forKey:recipientId inCollection:kOWSProfilesManager_UserWhitelistCollection];
[transaction setObject:@(YES) forKey:recipientId inCollection:kOWSProfileManager_UserWhitelistCollection];
self.userProfileWhitelistCache[recipientId] = @(YES);
}
}];
@ -477,7 +475,7 @@ static const NSInteger kProfileKeyLength = 16;
return [value boolValue];
}
value = @([self.dbConnection hasObjectForKey:recipientId inCollection:kOWSProfilesManager_UserWhitelistCollection]);
value = @([self.dbConnection hasObjectForKey:recipientId inCollection:kOWSProfileManager_UserWhitelistCollection]);
self.userProfileWhitelistCache[recipientId] = value;
return [value boolValue];
}
@ -487,7 +485,7 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert(groupId.length > 0);
NSString *groupIdKey = [groupId hexadecimalString];
[self.dbConnection setObject:@(1) forKey:groupIdKey inCollection:kOWSProfilesManager_GroupWhitelistCollection];
[self.dbConnection setObject:@(1) forKey:groupIdKey inCollection:kOWSProfileManager_GroupWhitelistCollection];
self.groupProfileWhitelistCache[groupIdKey] = @(YES);
}
@ -502,7 +500,7 @@ static const NSInteger kProfileKeyLength = 16;
}
value =
@(nil != [self.dbConnection objectForKey:groupIdKey inCollection:kOWSProfilesManager_GroupWhitelistCollection]);
@(nil != [self.dbConnection objectForKey:groupIdKey inCollection:kOWSProfileManager_GroupWhitelistCollection]);
self.groupProfileWhitelistCache[groupIdKey] = value;
return [value boolValue];
}
@ -514,10 +512,10 @@ static const NSInteger kProfileKeyLength = 16;
if (thread.isGroupThread) {
TSGroupThread *groupThread = (TSGroupThread *)thread;
NSData *groupId = groupThread.groupModel.groupId;
return [OWSProfilesManager.sharedManager isGroupIdInProfileWhitelist:groupId];
return [self isGroupIdInProfileWhitelist:groupId];
} else {
NSString *recipientId = thread.contactIdentifier;
return [OWSProfilesManager.sharedManager isUserInProfileWhitelist:recipientId];
return [self isUserInProfileWhitelist:recipientId];
}
}
@ -525,7 +523,7 @@ static const NSInteger kProfileKeyLength = 16;
{
OWSAssert([NSThread isMainThread]);
OWSAssert(contactRecipientIds);
// TODO: The persisted whitelist could either be:
//
// * Just users manually added to the whitelist.
@ -544,35 +542,29 @@ static const NSInteger kProfileKeyLength = 16;
return;
}
NSData *_Nullable existingProfileKey = [self profileKeyForRecipientId:recipientId];
if (existingProfileKey &&
[existingProfileKey isEqual:profileKey]) {
// Ignore redundant update.
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
OWSAssert(userProfile);
if (userProfile.profileKey && [userProfile.profileKey isEqual:profileKey]) {
// Ignore redundant update.
return;
}
userProfile.profileKey = profileKey;
[self.dbConnection setObject:profileKey
forKey:recipientId
inCollection:kOWSProfilesManager_OtherUsersProfileKeysCollection];
self.otherUsersProfileKeyCache[recipientId] = profileKey;
[self saveUserProfile:userProfile];
[self refreshProfileForRecipientId:recipientId ignoreThrottling:YES];
});
}
- (nullable NSData *)profileKeyForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
NSData *_Nullable profileKey = self.otherUsersProfileKeyCache[recipientId];
if (profileKey.length > 0) {
return profileKey;
}
profileKey =
[self.dbConnection dataForKey:recipientId inCollection:kOWSProfilesManager_OtherUsersProfileKeysCollection];
if (profileKey) {
OWSAssert(profileKey.length == kProfileKeyLength);
self.otherUsersProfileKeyCache[recipientId] = profileKey;
}
return profileKey;
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
OWSAssert(userProfile);
return userProfile.profileKey;
}
- (nullable NSString *)profileNameForRecipientId:(NSString *)recipientId
@ -580,9 +572,9 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert([NSThread isMainThread]);
OWSAssert(recipientId.length > 0);
[self fetchProfileForRecipientId:recipientId];
[self refreshProfileForRecipientId:recipientId];
UserProfile *userProfile = [self getOrCreateUserProfileForRecipientId:recipientId];
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
return userProfile.profileName;
}
@ -591,29 +583,52 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert([NSThread isMainThread]);
OWSAssert(recipientId.length > 0);
[self fetchProfileForRecipientId:recipientId];
[self refreshProfileForRecipientId:recipientId];
UIImage *_Nullable image = [self.otherUsersProfileAvatarImageCache objectForKey:recipientId];
if (image) {
return image;
}
UserProfile *userProfile = [self getOrCreateUserProfileForRecipientId:recipientId];
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
if (userProfile.avatarFileName) {
image = [self loadProfileAvatarWithFilename:userProfile.avatarFileName];
if (image) {
[self.otherUsersProfileAvatarImageCache setObject:image forKey:recipientId];
}
} else if (userProfile.avatarUrl) {
[self downloadProfileAvatarWithUrl:userProfile.avatarUrl recipientId:recipientId];
}
return image;
}
- (void)fetchProfileForRecipientId:(NSString *)recipientId
- (void)downloadProfileAvatarWithUrl:(NSString *)avatarUrl recipientId:(NSString *)recipientId
{
OWSAssert(avatarUrl.length > 0);
OWSAssert(recipientId.length > 0);
// TODO:
}
- (void)refreshProfileForRecipientId:(NSString *)recipientId
{
[self refreshProfileForRecipientId:recipientId ignoreThrottling:NO];
}
- (void)refreshProfileForRecipientId:(NSString *)recipientId ignoreThrottling:(BOOL)ignoreThrottling
{
OWSAssert([NSThread isMainThread]);
OWSAssert(recipientId.length > 0);
UserProfile *userProfile = [self getOrCreateUserProfileForRecipientId:recipientId];
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
if (!userProfile.profileKey) {
// There's no point in fetching the profile for a user
// if we don't have their profile key; we won't be able
// to decrypt it.
return;
}
// Throttle and debounce the updates.
const NSTimeInterval kMaxRefreshFrequency = 5 * kMinuteInterval;
@ -623,11 +638,154 @@ static const NSInteger kProfileKeyLength = 16;
}
userProfile.lastUpdateDate = [NSDate new];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[userProfile saveWithTransaction:transaction];
}];
// TODO: Actually update the profile.
[self saveUserProfile:userProfile];
[ProfileFetcherJob runWithRecipientId:recipientId
networkManager:self.networkManager
ignoreThrottling:ignoreThrottling];
}
- (void)updateProfileForRecipientId:(NSString *)recipientId
profileNameEncrypted:(NSData *_Nullable)profileNameEncrypted
avatarUrlEncrypted:(NSData *_Nullable)avatarUrlEncrypted
avatarDigestEncrypted:(NSData *_Nullable)avatarDigestEncrypted
{
OWSAssert(recipientId.length > 0);
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
if (!userProfile.profileKey) {
return;
}
NSString *_Nullable profileName =
[self decryptProfileString:profileNameEncrypted profileKey:userProfile.profileKey];
NSString *_Nullable avatarUrl = [self decryptProfileString:avatarUrlEncrypted profileKey:userProfile.profileKey];
NSData *_Nullable avatarDigest = [self decryptProfileData:avatarDigestEncrypted profileKey:userProfile.profileKey];
if (!avatarUrl || !avatarDigest) {
// If either avatar url or digest is missing, skip both.
avatarUrl = nil;
avatarDigest = nil;
}
BOOL isAvatarSame = ([self isNullableStringEqual:userProfile.avatarUrl toString:avatarUrl] &&
[self isNullableDataEqual:userProfile.avatarDigest toData:avatarDigest]);
dispatch_async(dispatch_get_main_queue(), ^{
userProfile.profileName = profileName;
userProfile.avatarUrl = avatarUrl;
userProfile.avatarDigest = avatarDigest;
if (!isAvatarSame) {
// Evacuate avatar image cache.
[self.otherUsersProfileAvatarImageCache removeObjectForKey:recipientId];
if (avatarUrl) {
[self downloadProfileAvatarWithUrl:avatarUrl recipientId:recipientId];
}
}
userProfile.lastUpdateDate = [NSDate new];
[self saveUserProfile:userProfile];
});
}
- (BOOL)isNullableDataEqual:(NSData *_Nullable)left toData:(NSData *_Nullable)right
{
if (left == nil && right == nil) {
return YES;
} else if (left == nil || right == nil) {
return YES;
} else {
return [left isEqual:right];
}
}
- (BOOL)isNullableStringEqual:(NSString *_Nullable)left toString:(NSString *_Nullable)right
{
if (left == nil && right == nil) {
return YES;
} else if (left == nil || right == nil) {
return YES;
} else {
return [left isEqualToString:right];
}
}
#pragma mark - Profile Encryption
+ (NSData *_Nullable)decryptProfileData:(NSData *_Nullable)encryptedData profileKey:(NSData *)profileKey
{
OWSAssert(profileKey.length == kProfileKeyLength);
if (!encryptedData) {
return nil;
}
// TODO: Decrypt.
return nil;
}
+ (NSString *_Nullable)decryptProfileString:(NSData *_Nullable)encryptedData profileKey:(NSData *)profileKey
{
OWSAssert(profileKey.length == kProfileKeyLength);
NSData *_Nullable decryptedData = [self decryptProfileData:encryptedData profileKey:profileKey];
if (decryptedData) {
return [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
} else {
return nil;
}
}
+ (NSData *_Nullable)encryptProfileData:(NSData *_Nullable)data profileKey:(NSData *)profileKey
{
OWSAssert(profileKey.length == kProfileKeyLength);
if (!data) {
return nil;
}
// TODO: Encrypt.
return nil;
}
+ (NSData *_Nullable)encryptProfileString:(NSString *_Nullable)value profileKey:(NSData *)profileKey
{
OWSAssert(profileKey.length == kProfileKeyLength);
if (value) {
NSData *_Nullable data = [value dataUsingEncoding:NSUTF8StringEncoding];
if (data) {
NSData *_Nullable encryptedData = [self encryptProfileData:data profileKey:profileKey];
return encryptedData;
}
}
return nil;
}
- (NSData *_Nullable)decryptProfileData:(NSData *_Nullable)encryptedData profileKey:(NSData *)profileKey
{
return [OWSProfileManager decryptProfileData:encryptedData profileKey:profileKey];
}
- (NSString *_Nullable)decryptProfileString:(NSData *_Nullable)encryptedData profileKey:(NSData *)profileKey
{
return [OWSProfileManager decryptProfileString:encryptedData profileKey:profileKey];
}
- (NSData *_Nullable)encryptProfileData:(NSData *_Nullable)data
{
return [OWSProfileManager encryptProfileData:data profileKey:self.localProfileKey];
}
- (NSData *_Nullable)encryptProfileString:(NSString *_Nullable)value
{
return [OWSProfileManager encryptProfileString:value profileKey:self.localProfileKey];
}
#pragma mark - Avatar Disk Cache

@ -16,20 +16,27 @@ class ProfileFetcherJob: NSObject {
// This property is only accessed on the main queue.
static var fetchDateMap = [String: Date]()
let ignoreThrottling: Bool
public class func run(thread: TSThread, networkManager: TSNetworkManager) {
ProfileFetcherJob(networkManager: networkManager).run(thread: thread)
ProfileFetcherJob(networkManager: networkManager).run(recipientIds: thread.recipientIdentifiers)
}
public class func run(recipientId: String, networkManager: TSNetworkManager, ignoreThrottling: Bool) {
ProfileFetcherJob(networkManager: networkManager, ignoreThrottling:ignoreThrottling).run(recipientIds: [recipientId])
}
init(networkManager: TSNetworkManager) {
init(networkManager: TSNetworkManager, ignoreThrottling: Bool = false) {
self.networkManager = networkManager
self.storageManager = TSStorageManager.shared()
self.ignoreThrottling = ignoreThrottling
}
public func run(thread: TSThread) {
public func run(recipientIds: [String]) {
AssertIsOnMainThread()
DispatchQueue.main.async {
for recipientId in thread.recipientIdentifiers {
for recipientId in recipientIds {
self.updateProfile(recipientId: recipientId)
}
}
@ -61,15 +68,17 @@ class ProfileFetcherJob: NSObject {
public func getProfile(recipientId: String) -> Promise<SignalServiceProfile> {
AssertIsOnMainThread()
if let lastDate = ProfileFetcherJob.fetchDateMap[recipientId] {
let lastTimeInterval = fabs(lastDate.timeIntervalSinceNow)
// Don't check a profile more often than every N minutes.
//
// Only throttle profile fetch in production builds in order to
// facilitate debugging.
let kGetProfileMaxFrequencySeconds = _isDebugAssertConfiguration() ? 0 : 60.0 * 5.0
guard lastTimeInterval > kGetProfileMaxFrequencySeconds else {
return Promise(error: ProfileFetcherJobError.throttled(lastTimeInterval: lastTimeInterval))
if !ignoreThrottling {
if let lastDate = ProfileFetcherJob.fetchDateMap[recipientId] {
let lastTimeInterval = fabs(lastDate.timeIntervalSinceNow)
// Don't check a profile more often than every N minutes.
//
// Only throttle profile fetch in production builds in order to
// facilitate debugging.
let kGetProfileMaxFrequencySeconds = _isDebugAssertConfiguration() ? 0 : 60.0 * 5.0
guard lastTimeInterval > kGetProfileMaxFrequencySeconds else {
return Promise(error: ProfileFetcherJobError.throttled(lastTimeInterval: lastTimeInterval))
}
}
}
ProfileFetcherJob.fetchDateMap[recipientId] = Date()
@ -105,7 +114,10 @@ class ProfileFetcherJob: NSObject {
private func updateProfile(signalServiceProfile: SignalServiceProfile) {
verifyIdentityUpToDateAsync(recipientId: signalServiceProfile.recipientId, latestIdentityKey: signalServiceProfile.identityKey)
// Eventually we'll want to do more things with new SignalServiceProfile fields here.
OWSProfileManager.shared().updateProfile(forRecipientId : signalServiceProfile.recipientId,
profileNameEncrypted : signalServiceProfile.profileNameEncrypted,
avatarUrlEncrypted : signalServiceProfile.avatarUrlEncrypted,
avatarDigestEncrypted : signalServiceProfile.avatarDigestEncrypted)
}
private func verifyIdentityUpToDateAsync(recipientId: String, latestIdentityKey: Data) {
@ -126,14 +138,22 @@ struct SignalServiceProfile {
enum ValidationError: Error {
case invalid(description: String)
case invalidIdentityKey(description: String)
case invalidProfileName(description: String)
case invalidAvatarUrl(description: String)
case invalidAvatarDigest(description: String)
}
public let recipientId: String
public let identityKey: Data
public let profileNameEncrypted: Data?
public let avatarUrlEncrypted: Data?
public let avatarDigestEncrypted: Data?
init(recipientId: String, rawResponse: Any?) throws {
self.recipientId = recipientId
Logger.info("rawResponse: \(rawResponse)")
guard let responseDict = rawResponse as? [String: Any?] else {
throw ValidationError.invalid(description: "\(TAG) unexpected type: \(String(describing: rawResponse))")
}
@ -141,17 +161,42 @@ struct SignalServiceProfile {
guard let identityKeyString = responseDict["identityKey"] as? String else {
throw ValidationError.invalidIdentityKey(description: "\(TAG) missing identity key: \(String(describing: rawResponse))")
}
guard let identityKeyWithType = Data(base64Encoded: identityKeyString) else {
throw ValidationError.invalidIdentityKey(description: "\(TAG) unable to parse identity key: \(identityKeyString)")
}
let kIdentityKeyLength = 33
guard identityKeyWithType.count == kIdentityKeyLength else {
throw ValidationError.invalidIdentityKey(description: "\(TAG) malformed key \(identityKeyString) with decoded length: \(identityKeyWithType.count)")
}
var profileNameEncrypted: Data? = nil
if let profileNameString = responseDict["name"] as? String {
guard let data = Data(base64Encoded: profileNameString) else {
throw ValidationError.invalidProfileName(description: "\(TAG) unable to parse profile name: \(profileNameString)")
}
profileNameEncrypted = data
}
var avatarUrlEncrypted: Data? = nil
if let avatarUrlString = responseDict["avatar"] as? String {
guard let data = Data(base64Encoded: avatarUrlString) else {
throw ValidationError.invalidAvatarUrl(description: "\(TAG) unable to parse avatar URL: \(avatarUrlString)")
}
avatarUrlEncrypted = data
}
var avatarDigestEncrypted: Data? = nil
if let avatarDigestString = responseDict["avatarDigest"] as? String {
guard let data = Data(base64Encoded: avatarDigestString) else {
throw ValidationError.invalidAvatarDigest(description: "\(TAG) unable to parse avatar digest: \(avatarDigestString)")
}
avatarDigestEncrypted = data
}
// `removeKeyType` is an objc category method only on NSData, so temporarily cast.
self.identityKey = (identityKeyWithType as NSData).removeKeyType() as Data
self.profileNameEncrypted = profileNameEncrypted
self.avatarUrlEncrypted = avatarUrlEncrypted
self.avatarDigestEncrypted = avatarDigestEncrypted
}
}

@ -18,6 +18,7 @@
#import "OWSDatabaseMigration.h"
#import "OWSLogger.h"
#import "OWSMessageEditing.h"
#import "OWSProfileManager.h"
#import "OWSProgressView.h"
#import "OWSViewController.h"
#import "OWSWebRTCDataProtos.pb.h"

@ -11,6 +11,7 @@
#import "OWSAvatarBuilder.h"
#import "OWSBlockingManager.h"
#import "OWSContactsManager.h"
#import "OWSProfileManager.h"
#import "PhoneNumber.h"
#import "ShowGroupMembersViewController.h"
#import "Signal-Swift.h"
@ -24,7 +25,6 @@
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob.h>
#import <SignalServiceKit/OWSProfilesManager.h>
#import <SignalServiceKit/TSGroupThread.h>
#import <SignalServiceKit/TSOutgoingMessage.h>
#import <SignalServiceKit/TSStorageManager.h>
@ -294,7 +294,7 @@ NS_ASSUME_NONNULL_BEGIN
}]];
}
if ([OWSProfilesManager.sharedManager isThreadInProfileWhitelist:self.thread]) {
if ([OWSProfileManager.sharedManager isThreadInProfileWhitelist:self.thread]) {
[mainSection addItem:[OWSTableItem itemWithCustomCellBlock:^{
return [weakSelf
labelCellWithName:(self.isGroupThread
@ -804,10 +804,10 @@ NS_ASSUME_NONNULL_BEGIN
if (self.isGroupThread) {
TSGroupThread *groupThread = (TSGroupThread *)self.thread;
NSData *groupId = groupThread.groupModel.groupId;
[OWSProfilesManager.sharedManager addGroupIdToProfileWhitelist:groupId];
[OWSProfileManager.sharedManager addGroupIdToProfileWhitelist:groupId];
} else {
NSString *recipientId = self.thread.contactIdentifier;
[OWSProfilesManager.sharedManager addUserToProfileWhitelist:recipientId];
[OWSProfileManager.sharedManager addUserToProfileWhitelist:recipientId];
}
[self updateTableContents];

@ -4,12 +4,12 @@
#import "ProfileViewController.h"
#import "AvatarViewHelper.h"
#import "OWSProfileManager.h"
#import "Signal-Swift.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import "UIViewController+OWS.h"
#import <SignalServiceKit/OWSProfilesManager.h>
NS_ASSUME_NONNULL_BEGIN
@ -46,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
_avatarViewHelper = [AvatarViewHelper new];
_avatarViewHelper.delegate = self;
_avatar = [OWSProfilesManager.sharedManager localProfileAvatarImage];
_avatar = [OWSProfileManager.sharedManager localProfileAvatarImage];
[self createViews];
}
@ -59,7 +59,7 @@ NS_ASSUME_NONNULL_BEGIN
_nameTextField.placeholder = NSLocalizedString(
@"PROFILE_VIEW_NAME_DEFAULT_TEXT", @"Default text for the profile name field of the profile view.");
_nameTextField.delegate = self;
_nameTextField.text = [OWSProfilesManager.sharedManager localProfileName];
_nameTextField.text = [OWSProfileManager.sharedManager localProfileName];
[_nameTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
_avatarView = [AvatarImageView new];
@ -212,15 +212,14 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateProfile
{
__weak ProfileViewController *weakSelf = self;
[OWSProfilesManager.sharedManager
updateLocalProfileName:self.nameTextField.text
avatarImage:self.avatar
success:^{
[weakSelf.navigationController popViewControllerAnimated:YES];
}
failure:^{
// <#code#>
}];
[OWSProfileManager.sharedManager updateLocalProfileName:self.nameTextField.text
avatarImage:self.avatar
success:^{
[weakSelf.navigationController popViewControllerAnimated:YES];
}
failure:^{
// TODO: Handle failure.
}];
}
#pragma mark - UITextFieldDelegate

@ -4,12 +4,12 @@
#import "OWSContactsManager.h"
#import "Environment.h"
#import "OWSProfileManager.h"
#import "Signal-Swift.h"
#import "Util.h"
#import "ViewControllerUtils.h"
#import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/OWSProfilesManager.h>
#import <SignalServiceKit/SignalAccount.h>
#import <SignalServiceKit/TSStorageManager.h>
@ -206,7 +206,7 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account
self.signalAccountMap = [signalAccountMap copy];
self.signalAccounts = [signalAccounts copy];
[OWSProfilesManager.sharedManager setContactRecipientIds:signalAccountMap.allKeys];
[OWSProfileManager.sharedManager setContactRecipientIds:signalAccountMap.allKeys];
[self updateCachedDisplayNames];
});

@ -6,6 +6,7 @@
#import "TestUtil.h"
@implementation ExceptionsTest
- (void)testContracts {
ows_require(1 + 1 == 2);
@try {

@ -4,7 +4,6 @@
#import "OWSOutgoingSyncMessage.h"
#import "Cryptography.h"
#import "OWSProfilesManager.h"
#import "OWSSignalServiceProtos.pb.h"
NS_ASSUME_NONNULL_BEGIN

@ -5,12 +5,13 @@
#import "TSOutgoingMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSOutgoingSyncMessage.h"
#import "OWSProfilesManager.h"
#import "OWSSignalServiceProtos.pb.h"
#import "ProfileManagerProtocol.h"
#import "SignalRecipient.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
#import "TSGroupThread.h"
#import "TextSecureKitEnv.h"
#import <YapDatabase/YapDatabase.h>
#import <YapDatabase/YapDatabaseTransaction.h>
@ -464,7 +465,10 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
OWSAssert(contentBuilder);
OWSAssert(recipient);
OWSAssert(OWSProfilesManager.sharedManager.localProfileKey.length > 0);
id<ProfileManagerProtocol> profileManager = [TextSecureKitEnv sharedEnv].profileManager;
NSData *localProfileKey = profileManager.localProfileKey;
OWSAssert(localProfileKey.length > 0);
BOOL shouldIncludeProfileKey = NO;
if ([self isKindOfClass:[OWSOutgoingSyncMessage class]]) {
@ -478,15 +482,15 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
//
// For Group threads, we want to include the profile key IFF the
// recipient OR the group is in the whitelist.
if ([OWSProfilesManager.sharedManager isUserInProfileWhitelist:recipient.recipientId]) {
if ([profileManager isUserInProfileWhitelist:recipient.recipientId]) {
shouldIncludeProfileKey = YES;
} else if ([OWSProfilesManager.sharedManager isThreadInProfileWhitelist:self.thread]) {
} else if ([profileManager isThreadInProfileWhitelist:self.thread]) {
shouldIncludeProfileKey = YES;
}
}
if (shouldIncludeProfileKey) {
[contentBuilder setProfileKey:OWSProfilesManager.sharedManager.localProfileKey];
[contentBuilder setProfileKey:localProfileKey];
}
}

@ -6,7 +6,6 @@ NS_ASSUME_NONNULL_BEGIN
@class ContactsUpdater;
@class OWSBlockingManager;
@class OWSProfilesManager;
@class OWSUploadingService;
@class SignalRecipient;
@class TSInvalidIdentityKeySendingErrorMessage;
@ -59,8 +58,6 @@ NS_SWIFT_NAME(MessageSender)
- (void)setBlockingManager:(OWSBlockingManager *)blockingManager;
- (void)setProfilesManager:(OWSProfilesManager *)profilesManager;
/**
* Send and resend text messages or resend messages with existing attachments.
* If you haven't yet created the attachment, see the `sendAttachmentData:` variants.

@ -14,7 +14,6 @@
#import "OWSMessageServiceParams.h"
#import "OWSOutgoingSentMessageTranscript.h"
#import "OWSOutgoingSyncMessage.h"
#import "OWSProfilesManager.h"
#import "OWSUploadingService.h"
#import "PreKeyBundle+jsonDict.h"
#import "SignalRecipient.h"
@ -356,7 +355,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@property (nonatomic, readonly) TSNetworkManager *networkManager;
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
@property (nonatomic, readonly) OWSProfilesManager *profilesManager;
@property (nonatomic, readonly) OWSUploadingService *uploadingService;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@ -399,14 +397,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
_blockingManager = blockingManager;
}
- (void)setProfilesManager:(OWSProfilesManager *)profilesManager
{
OWSAssert(profilesManager);
OWSAssert(!_profilesManager);
_profilesManager = profilesManager;
}
- (NSOperationQueue *)sendingQueueForMessage:(TSOutgoingMessage *)message
{
OWSAssert(message);

@ -9,7 +9,6 @@
#import "OWSCallHangupMessage.h"
#import "OWSCallIceUpdateMessage.h"
#import "OWSCallOfferMessage.h"
#import "OWSProfilesManager.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSContactThread.h"

@ -6,7 +6,6 @@
#import "Cryptography.h"
#import "NSDate+millisecondTimeStamp.h"
#import "TSContactThread.h"
#import "OWSProfilesManager.h"
#import "OWSSignalServiceProtos.pb.h"
#import "OWSVerificationStateSyncMessage.h"

@ -19,12 +19,12 @@
#import "OWSIncomingMessageFinder.h"
#import "OWSIncomingSentMessageTranscript.h"
#import "OWSMessageSender.h"
#import "OWSProfilesManager.h"
#import "OWSReadReceiptsProcessor.h"
#import "OWSRecordTranscriptJob.h"
#import "OWSSyncContactsMessage.h"
#import "OWSSyncGroupsMessage.h"
#import "OWSSyncGroupsRequestMessage.h"
#import "ProfileManagerProtocol.h"
#import "TSAccountManager.h"
#import "TSAttachmentStream.h"
#import "TSCall.h"
@ -499,8 +499,8 @@ NS_ASSUME_NONNULL_BEGIN
if ([content hasProfileKey]) {
NSData *profileKey = [content profileKey];
NSString *recipientId = envelope.source;
[OWSProfilesManager.sharedManager setProfileKey:profileKey
forRecipientId:recipientId];
id<ProfileManagerProtocol> profileManager = [TextSecureKitEnv sharedEnv].profileManager;
[profileManager setProfileKey:profileKey forRecipientId:recipientId];
}
if (content.hasSyncMessage) {

@ -0,0 +1,17 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
@class TSThread;
@protocol ProfileManagerProtocol <NSObject>
- (NSData *)localProfileKey;
- (void)setProfileKey:(NSData *)profileKey forRecipientId:(NSString *)recipientId;
- (BOOL)isUserInProfileWhitelist:(NSString *)recipientId;
- (BOOL)isThreadInProfileWhitelist:(TSThread *)thread;
@end

@ -8,13 +8,15 @@ NS_ASSUME_NONNULL_BEGIN
@class OWSMessageSender;
@protocol NotificationsProtocol;
@protocol OWSCallMessageHandler;
@protocol ProfileManagerProtocol;
@interface TextSecureKitEnv : NSObject
- (instancetype)initWithCallMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager
messageSender:(OWSMessageSender *)messageSender
notificationsManager:(id<NotificationsProtocol>)notificationsManager NS_DESIGNATED_INITIALIZER;
notificationsManager:(id<NotificationsProtocol>)notificationsManager
profileManager:(id<ProfileManagerProtocol>)profileManager NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@ -25,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) id<NotificationsProtocol> notificationsManager;
@property (nonatomic, readonly) id<ProfileManagerProtocol> profileManager;
@end

@ -11,12 +11,14 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
@implementation TextSecureKitEnv
@synthesize callMessageHandler = _callMessageHandler, contactsManager = _contactsManager,
messageSender = _messageSender, notificationsManager = _notificationsManager;
messageSender = _messageSender, notificationsManager = _notificationsManager,
profileManager = _profileManager;
- (instancetype)initWithCallMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager
messageSender:(OWSMessageSender *)messageSender
notificationsManager:(id<NotificationsProtocol>)notificationsManager
profileManager:(id<ProfileManagerProtocol>)profileManager
{
self = [super init];
if (!self) {
@ -27,6 +29,7 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
_contactsManager = contactsManager;
_messageSender = messageSender;
_notificationsManager = notificationsManager;
_profileManager = profileManager;
return self;
}
@ -71,6 +74,12 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
return _notificationsManager;
}
- (id<ProfileManagerProtocol>)profileManager
{
NSAssert(_profileManager, @"Trying to access the profileManager before it's set.");
return _profileManager;
}
@end
NS_ASSUME_NONNULL_END

Loading…
Cancel
Save