//// //// Copyright (c) 2019 Open Whisper Systems. All rights reserved. //// // //#import "OWSProfileManager.h" //#import "Environment.h" //#import "OWSUserProfile.h" //#import //#import //#import "UIUtil.h" //#import //#import //#import //#import //#import //#import //#import //#import //#import //#import //#import //#import //#import //#import // //NS_ASSUME_NONNULL_BEGIN // //// The max bytes for a user's profile name, encoded in UTF8. //// Before encrypting and submitting we NULL pad the name data to this length. //const NSUInteger kOWSProfileManager_NameDataLength = 26; //const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; // //typedef void (^ProfileManagerFailureBlock)(NSError *error); // //@interface OWSProfileManager () // //@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; // //// This property can be accessed on any thread, while synchronized on self. //@property (atomic, readonly) NSCache *profileAvatarImageCache; // //// This property can be accessed on any thread, while synchronized on self. //@property (atomic, readonly) NSMutableSet *currentAvatarDownloads; // //@end // //#pragma mark - // //// Access to most state should happen while synchronized on the profile manager. //// Writes should happen off the main thread, wherever possible. //@implementation OWSProfileManager // //+ (instancetype)sharedManager //{ // return SSKEnvironment.shared.profileManager; //} // //- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage //{ // self = [super init]; // // if (!self) { // return self; // } // // OWSAssertIsOnMainThread(); // OWSAssertDebug(primaryStorage); // // _dbConnection = primaryStorage.newDatabaseConnection; // // _profileAvatarImageCache = [NSCache new]; // _currentAvatarDownloads = [NSMutableSet new]; // // OWSSingletonAssert(); // // return self; //} // //- (void)dealloc //{ // [[NSNotificationCenter defaultCenter] removeObserver:self]; //} // //#pragma mark - Dependencies // //- (TSAccountManager *)tsAccountManager //{ // return TSAccountManager.sharedInstance; //} // //- (OWSIdentityManager *)identityManager //{ // return SSKEnvironment.shared.identityManager; //} // //- (void)updateLocalProfileName:(nullable NSString *)profileName // avatarImage:(nullable UIImage *)avatarImage // success:(void (^)(void))successBlockParameter // failure:(void (^)(NSError *))failureBlockParameter // requiresSync:(BOOL)requiresSync //{ // OWSAssertDebug(successBlockParameter); // OWSAssertDebug(failureBlockParameter); // // // Ensure that the success and failure blocks are called on the main thread. // void (^failureBlock)(NSError *) = ^(NSError *error) { // OWSLogError(@"Updating service with profile failed."); // // dispatch_async(dispatch_get_main_queue(), ^{ // failureBlockParameter(error); // }); // }; // void (^successBlock)(void) = ^{ // OWSLogInfo(@"Successfully updated service with profile."); // // dispatch_async(dispatch_get_main_queue(), ^{ // successBlockParameter(); // }); // }; // // // The final steps are to: // // // // * Try to update the service. // // * Update client state on success. // void (^tryToUpdateService)(NSString *_Nullable, NSString *_Nullable) = ^( // NSString *_Nullable avatarUrlPath, NSString *_Nullable avatarFileName) { // [self updateServiceWithProfileName:profileName // avatarUrl:avatarUrlPath // success:^{ // SNContact *userProfile = [LKStorage.shared getUser]; // OWSAssertDebug(userProfile); // // userProfile.name = profileName; // userProfile.profilePictureURL = avatarUrlPath; // userProfile.profilePictureFileName = avatarFileName; // // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:userProfile usingTransaction:transaction]; // } completion:^{ // if (avatarFileName != nil) { // [self updateProfileAvatarCache:avatarImage filename:avatarFileName]; // } // // successBlock(); // }]; // } // failure:^(NSError *error) { // failureBlock(error); // }]; // }; // // SNContact *userProfile = [LKStorage.shared getUser]; // OWSAssertDebug(userProfile); // // if (avatarImage) { // // If we have a new avatar image, we must first: // // // // * Encode it to JPEG. // // * Write it to disk. // // * Encrypt it // // * Upload it to asset service // // * Send asset service info to Signal Service // OWSLogVerbose(@"Updating local profile on service with new avatar."); // [self writeAvatarToDisk:avatarImage // success:^(NSData *data, NSString *fileName) { // [self uploadAvatarToService:data // success:^(NSString *_Nullable avatarUrlPath) { // tryToUpdateService(avatarUrlPath, fileName); // } // failure:^(NSError *error) { // failureBlock(error); // }]; // } // failure:^(NSError *error) { // failureBlock(error); // }]; // } else if (userProfile.profilePictureURL) { // OWSLogVerbose(@"Updating local profile on service with cleared avatar."); // [self uploadAvatarToService:nil // success:^(NSString *_Nullable avatarUrlPath) { // tryToUpdateService(nil, nil); // } // failure:^(NSError *error) { // failureBlock(error); // }]; // } else { // OWSLogVerbose(@"Updating local profile on service with no avatar."); // tryToUpdateService(nil, nil); // } //} // //- (void)writeAvatarToDisk:(UIImage *)avatar // success:(void (^)(NSData *data, NSString *fileName))successBlock // failure:(ProfileManagerFailureBlock)failureBlock { // OWSAssertDebug(avatar); // OWSAssertDebug(successBlock); // OWSAssertDebug(failureBlock); // // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // if (avatar) { // NSData *data = [self processedImageDataForRawAvatar:avatar]; // OWSAssertDebug(data); // if (data) { // NSString *fileName = [self generateAvatarFilename]; // NSString *filePath = [OWSUserProfile profileAvatarFilepathWithFilename:fileName]; // BOOL success = [data writeToFile:filePath atomically:YES]; // OWSAssertDebug(success); // if (success) { // return successBlock(data, fileName); // } // } // } // failureBlock(OWSErrorWithCodeDescription(OWSErrorCodeAvatarWriteFailed, @"Avatar write failed.")); // }); //} // //- (NSData *)processedImageDataForRawAvatar:(UIImage *)image //{ // NSUInteger kMaxAvatarBytes = 5 * 1000 * 1000; // // if (image.size.width != kOWSProfileManager_MaxAvatarDiameter // || image.size.height != kOWSProfileManager_MaxAvatarDiameter) { // // To help ensure the user is being shown the same cropping of their avatar as // // everyone else will see, we want to be sure that the image was resized before this point. // OWSFailDebug(@"Avatar image should have been resized before trying to upload"); // image = [image resizedImageToFillPixelSize:CGSizeMake(kOWSProfileManager_MaxAvatarDiameter, // kOWSProfileManager_MaxAvatarDiameter)]; // } // // NSData *_Nullable data = UIImageJPEGRepresentation(image, 0.95f); // if (data.length > kMaxAvatarBytes) { // // Our avatar dimensions are so small that it's incredibly unlikely we wouldn't be able to fit our profile // // photo. e.g. generating pure noise at our resolution compresses to ~200k. // OWSFailDebug(@"Suprised to find profile avatar was too large. Was it scaled properly? image: %@", image); // } // // return data; //} // //// If avatarData is nil, we are clearing the avatar. //- (void)uploadAvatarToService:(NSData *_Nullable)avatarData // success:(void (^)(NSString *_Nullable avatarUrlPath))successBlock // failure:(ProfileManagerFailureBlock)failureBlock { // OWSAssertDebug(successBlock); // OWSAssertDebug(failureBlock); // OWSAssertDebug(avatarData == nil || avatarData.length > 0); // // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // // We always want to encrypt a profile with a new profile key // // This ensures that other users know that our profile picture was updated // OWSAES256Key *newProfileKey = [OWSAES256Key generateRandomKey]; // // if (avatarData) { // NSData *encryptedAvatarData = [self encryptProfileData:avatarData profileKey:newProfileKey]; // OWSAssertDebug(encryptedAvatarData.length > 0); // // AnyPromise *promise = [SNFileServerAPIV2 upload:encryptedAvatarData]; // // [promise.thenOn(dispatch_get_main_queue(), ^(NSString *fileID) { // NSString *downloadURL = [NSString stringWithFormat:@"%@/files/%@", SNFileServerAPIV2.server, fileID]; // [NSUserDefaults.standardUserDefaults setObject:[NSDate new] forKey:@"lastProfilePictureUpload"]; // // SNContact *user = [LKStorage.shared getUser]; // user.profileEncryptionKey = newProfileKey; // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:user usingTransaction:transaction]; // } completion:^{ // successBlock(downloadURL); // }]; // }) // .catchOn(dispatch_get_main_queue(), ^(id result) { // // There appears to be a bug in PromiseKit that sometimes causes catchOn // // to be invoked with the fulfilled promise's value as the error. The below // // is a quick and dirty workaround. // if ([result isKindOfClass:NSString.class]) { // SNContact *user = [LKStorage.shared getUser]; // user.profileEncryptionKey = newProfileKey; // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:user usingTransaction:transaction]; // } completion:^{ // successBlock(result); // }]; // } else { // failureBlock(result); // } // }) retainUntilComplete]; // } else { // // Update our profile key and set the url to nil if avatar data is nil // SNContact *user = [LKStorage.shared getUser]; // user.profileEncryptionKey = newProfileKey; // user.profilePictureURL = nil; // user.profilePictureFileName = nil; // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:user usingTransaction:transaction]; // } completion:^{ // successBlock(nil); // }]; // } // }); //} // //- (void)updateServiceWithProfileName:(nullable NSString *)localProfileName // avatarUrl:(nullable NSString *)avatarURL // success:(void (^)(void))successBlock // failure:(ProfileManagerFailureBlock)failureBlock { // successBlock(); //} // //- (void)updateServiceWithProfileName:(nullable NSString *)localProfileName avatarURL:(nullable NSString *)avatarURL { // [self updateServiceWithProfileName:localProfileName avatarUrl:avatarURL success:^{} failure:^(NSError * _Nonnull error) {}]; //} // //#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]; // } // return [groupId copy]; //} // //- (void)regenerateLocalProfile //{ // NSString *userPublicKey = [SNGeneralUtilities getUserPublicKey]; // SNContact *contact = [LKStorage.shared getContactWithSessionID:userPublicKey]; // contact.profileEncryptionKey = [OWSAES256Key generateRandomKey]; // contact.profilePictureURL = nil; // contact.profilePictureFileName = nil; // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:contact usingTransaction:transaction]; // } completion:^{ // [[self.tsAccountManager updateAccountAttributes] retainUntilComplete]; // }]; //} // //#pragma mark - Other Users' Profiles // //- (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId avatarURL:(nullable NSString *)avatarURL //{ // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // OWSAES256Key *_Nullable profileKey = [OWSAES256Key keyWithData:profileKeyData]; // if (profileKey == nil) { // OWSFailDebug(@"Failed to make profile key for key data"); // return; // } // // SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId]; // // OWSAssertDebug(contact); // if (contact.profileEncryptionKey != nil && [contact.profileEncryptionKey.keyData isEqual:profileKey.keyData]) { // // Ignore redundant update. // return; // } // // contact.profileEncryptionKey = profileKey; // contact.profilePictureURL = nil; // contact.profilePictureFileName = nil; // // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:contact usingTransaction:transaction]; // } completion:^{ // contact.profilePictureURL = avatarURL; // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:contact usingTransaction:transaction]; // } completion:^{ // [self downloadAvatarForUserProfile:contact]; // }]; // }]; // }); //} // //- (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId //{ // [self setProfileKeyData:profileKeyData forRecipientId:recipientId avatarURL:nil]; //} // //- (nullable NSData *)profileKeyDataForRecipientId:(NSString *)recipientId //{ // return [self profileKeyForRecipientId:recipientId].keyData; //} // //- (nullable OWSAES256Key *)profileKeyForRecipientId:(NSString *)recipientId //{ // OWSAssertDebug(recipientId.length > 0); // // SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId]; // OWSAssertDebug(contact); // // return contact.profileEncryptionKey; //} // //- (nullable UIImage *)profileAvatarForRecipientId:(NSString *)recipientId //{ // OWSAssertDebug(recipientId.length > 0); // // SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId]; // // if (contact.profilePictureFileName != nil && contact.profilePictureFileName.length > 0) { // return [self loadProfileAvatarWithFilename:contact.profilePictureFileName]; // } // // if (contact.profilePictureURL != nil && contact.profilePictureURL.length > 0) { // [self downloadAvatarForUserProfile:contact]; // } // // return nil; //} // //- (nullable NSData *)profileAvatarDataForRecipientId:(NSString *)recipientId //{ // OWSAssertDebug(recipientId.length > 0); // // SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId]; // // if (contact.profilePictureFileName != nil && contact.profilePictureFileName.length > 0) { // return [self loadProfileDataWithFilename:contact.profilePictureFileName]; // } // // return nil; //} // //- (NSString *)generateAvatarFilename //{ // return [[NSUUID UUID].UUIDString stringByAppendingPathExtension:@"jpg"]; //} // //- (void)downloadAvatarForUserProfile:(SNContact *)contact //{ // OWSAssertDebug(contact); // // __block OWSBackgroundTask *backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; // // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // BOOL hasProfilePictureURL = (contact.profilePictureURL != nil && contact.profilePictureURL.length > 0); // if (!hasProfilePictureURL) { // OWSLogDebug(@"Skipping downloading avatar for %@ because url is not set", contact.sessionID); // return; // } // NSString *_Nullable avatarUrlPathAtStart = contact.profilePictureURL; // // BOOL hasProfileEncryptionKey = (contact.profileEncryptionKey != nil && contact.profileEncryptionKey.keyData.length > 0); // if (!hasProfileEncryptionKey || !hasProfilePictureURL) { // return; // } // // OWSAES256Key *profileKeyAtStart = contact.profileEncryptionKey; // // NSString *fileName = [self generateAvatarFilename]; // NSString *filePath = [OWSUserProfile profileAvatarFilepathWithFilename:fileName]; // // @synchronized(self.currentAvatarDownloads) // { // if ([self.currentAvatarDownloads containsObject:contact.sessionID]) { // // Download already in flight; ignore. // return; // } // [self.currentAvatarDownloads addObject:contact.sessionID]; // } // // OWSLogVerbose(@"downloading profile avatar: %@", contact.sessionID); // // NSString *profilePictureURL = contact.profilePictureURL; // // NSString *file = [profilePictureURL lastPathComponent]; // BOOL useOldServer = [profilePictureURL containsString:SNFileServerAPIV2.oldServer]; // AnyPromise *promise = [SNFileServerAPIV2 download:file useOldServer:useOldServer]; // // [promise.then(^(NSData *data) { // @synchronized(self.currentAvatarDownloads) // { // [self.currentAvatarDownloads removeObject:contact.sessionID]; // } // NSData *_Nullable encryptedData = data; // NSData *_Nullable decryptedData = [self decryptProfileData:encryptedData profileKey:profileKeyAtStart]; // UIImage *_Nullable image = nil; // if (decryptedData) { // BOOL success = [decryptedData writeToFile:filePath atomically:YES]; // if (success) { // image = [UIImage imageWithContentsOfFile:filePath]; // } // } // // SNContact *latestContact = [LKStorage.shared getContactWithSessionID:contact.sessionID]; // // BOOL hasProfileEncryptionKey = (latestContact.profileEncryptionKey != nil // && latestContact.profileEncryptionKey.keyData.length > 0); // if (!hasProfileEncryptionKey || ![latestContact.profileEncryptionKey isEqual:contact.profileEncryptionKey]) { // OWSLogWarn(@"Ignoring avatar download for obsolete user profile."); // } else if (![avatarUrlPathAtStart isEqualToString:latestContact.profilePictureURL]) { // OWSLogInfo(@"avatar url has changed during download"); // if (latestContact.profilePictureURL != nil && latestContact.profilePictureURL.length > 0) { // [self downloadAvatarForUserProfile:latestContact]; // } // } else if (!encryptedData) { // OWSLogError(@"avatar encrypted data for %@ could not be read.", contact.sessionID); // } else if (!decryptedData) { // OWSLogError(@"avatar data for %@ could not be decrypted.", contact.sessionID); // } else if (!image) { // OWSLogError(@"avatar image for %@ could not be loaded.", contact.sessionID); // } else { // latestContact.profilePictureFileName = fileName; // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:latestContact usingTransaction:transaction]; // }]; // [self updateProfileAvatarCache:image filename:fileName]; // } // // OWSAssertDebug(backgroundTask); // backgroundTask = nil; // }) retainUntilComplete]; // }); //} // //- (void)updateProfileForRecipientId:(NSString *)recipientId // profileNameEncrypted:(nullable NSData *)profileNameEncrypted // avatarUrlPath:(nullable NSString *)avatarUrlPath //{ // OWSAssertDebug(recipientId.length > 0); // // OWSLogDebug(@"update profile for: %@ name: %@ avatar: %@", recipientId, profileNameEncrypted, avatarUrlPath); // // // Ensure decryption, etc. off main thread. // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId]; // // if (!contact.profileEncryptionKey) { return; } // // NSString *_Nullable profileName = // [self decryptProfileNameData:profileNameEncrypted profileKey:contact.profileEncryptionKey]; // // contact.name = profileName; // contact.profilePictureURL = avatarUrlPath; // // [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // [LKStorage.shared setContact:contact usingTransaction:transaction]; // }]; // // // Whenever we change avatarUrlPath, OWSUserProfile clears avatarFileName. // // So if avatarUrlPath is set and avatarFileName is not set, we should to // // download this avatar. downloadAvatarForUserProfile will de-bounce // // downloads. // BOOL hasProfilePictureURL = (contact.profilePictureURL != nil && contact.profilePictureURL.length > 0); // BOOL hasProfilePictureFileName = (contact.profilePictureFileName != nil && contact.profilePictureFileName.length > 0); // if (hasProfilePictureURL && !hasProfilePictureFileName) { // [self downloadAvatarForUserProfile:contact]; // } // }); //} // //- (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 // //- (nullable NSData *)encryptProfileData:(nullable NSData *)encryptedData profileKey:(OWSAES256Key *)profileKey //{ // OWSAssertDebug(profileKey.keyData.length == kAES256_KeyByteLength); // // if (!encryptedData) { // return nil; // } // // return [Cryptography encryptAESGCMWithProfileData:encryptedData key:profileKey]; //} // //- (nullable NSData *)decryptProfileData:(nullable NSData *)encryptedData profileKey:(OWSAES256Key *)profileKey //{ // OWSAssertDebug(profileKey.keyData.length == kAES256_KeyByteLength); // // if (!encryptedData) { // return nil; // } // // return [Cryptography decryptAESGCMWithProfileData:encryptedData key:profileKey]; //} // //- (nullable NSString *)decryptProfileNameData:(nullable NSData *)encryptedData profileKey:(OWSAES256Key *)profileKey //{ // OWSAssertDebug(profileKey.keyData.length == kAES256_KeyByteLength); // // NSData *_Nullable decryptedData = [self decryptProfileData:encryptedData profileKey:profileKey]; // if (decryptedData.length < 1) { // return nil; // } // // // // Unpad profile name. // NSUInteger unpaddedLength = 0; // const char *bytes = decryptedData.bytes; // // // Work through the bytes until we encounter our first // // padding byte (our padding scheme is NULL bytes) // for (NSUInteger i = 0; i < decryptedData.length; i++) { // if (bytes[i] == 0x00) { // break; // } // unpaddedLength = i + 1; // } // // NSData *unpaddedData = [decryptedData subdataWithRange:NSMakeRange(0, unpaddedLength)]; // // return [[NSString alloc] initWithData:unpaddedData encoding:NSUTF8StringEncoding]; //} // //- (nullable NSData *)encryptProfileData:(nullable NSData *)data //{ // OWSAES256Key *localProfileKey = [LKStorage.shared getUser].profileEncryptionKey; // // return [self encryptProfileData:data profileKey:localProfileKey]; //} // //- (BOOL)isProfileNameTooLong:(nullable NSString *)profileName //{ // OWSAssertIsOnMainThread(); // // NSData *nameData = [profileName dataUsingEncoding:NSUTF8StringEncoding]; // return nameData.length > kOWSProfileManager_NameDataLength; //} // //- (nullable NSData *)encryptProfileNameWithUnpaddedName:(NSString *)name //{ // NSData *nameData = [name dataUsingEncoding:NSUTF8StringEncoding]; // if (nameData.length > kOWSProfileManager_NameDataLength) { // OWSFailDebug(@"name data is too long with length:%lu", (unsigned long)nameData.length); // return nil; // } // // NSUInteger paddingByteCount = kOWSProfileManager_NameDataLength - nameData.length; // // NSMutableData *paddedNameData = [nameData mutableCopy]; // // Since we want all encrypted profile names to be the same length on the server, we use `increaseLengthBy` // // to pad out any remaining length with 0 bytes. // [paddedNameData increaseLengthBy:paddingByteCount]; // OWSAssertDebug(paddedNameData.length == kOWSProfileManager_NameDataLength); // // OWSAES256Key *localProfileKey = [LKStorage.shared getUser].profileEncryptionKey; // // return [self encryptProfileData:[paddedNameData copy] profileKey:localProfileKey]; //} // //#pragma mark - Avatar Disk Cache // //- (nullable NSData *)loadProfileDataWithFilename:(NSString *)filename //{ // if (filename.length <= 0) { return nil; }; // // NSString *filePath = [OWSUserProfile profileAvatarFilepathWithFilename:filename]; // return [NSData dataWithContentsOfFile:filePath]; //} // //- (nullable UIImage *)loadProfileAvatarWithFilename:(NSString *)filename //{ // if (filename.length == 0) { // return nil; // } // // UIImage *_Nullable image = nil; // @synchronized(self.profileAvatarImageCache) // { // image = [self.profileAvatarImageCache objectForKey:filename]; // } // if (image) { // return image; // } // // NSData *data = [self loadProfileDataWithFilename:filename]; // if (![data ows_isValidImage]) { // return nil; // } // image = [UIImage imageWithData:data]; // [self updateProfileAvatarCache:image filename:filename]; // return image; //} // //- (void)updateProfileAvatarCache:(nullable UIImage *)image filename:(NSString *)filename //{ // if (filename.length <= 0) { return; }; // // @synchronized(self.profileAvatarImageCache) // { // if (image) { // [self.profileAvatarImageCache setObject:image forKey:filename]; // } else { // [self.profileAvatarImageCache removeObjectForKey:filename]; // } // } //} // //@end // //NS_ASSUME_NONNULL_END