Download profile avatars.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 9266c3a4f9
commit f6668d24c1

@ -112,6 +112,9 @@ static const NSInteger kProfileKeyLength = 16;
// This property should only be mutated on the main thread,
@property (nonatomic, readonly) NSCache<NSString *, UIImage *> *otherUsersProfileAvatarImageCache;
// This property should only be mutated on the main thread,
@property (atomic, readonly) NSMutableSet<NSString *> *currentAvatarDownloads;
@end
#pragma mark -
@ -159,6 +162,7 @@ static const NSInteger kProfileKeyLength = 16;
_userProfileWhitelistCache = [NSMutableDictionary new];
_groupProfileWhitelistCache = [NSMutableDictionary new];
_otherUsersProfileAvatarImageCache = [NSCache new];
_currentAvatarDownloads = [NSMutableSet new];
OWSSingletonAssert();
@ -632,18 +636,106 @@ static const NSInteger kProfileKeyLength = 16;
[self.otherUsersProfileAvatarImageCache setObject:image forKey:recipientId];
}
} else if (userProfile.avatarUrl) {
[self downloadProfileAvatarWithUrl:userProfile.avatarUrl recipientId:recipientId];
[self downloadAvatarForUserProfile:userProfile];
}
return image;
}
- (void)downloadProfileAvatarWithUrl:(NSString *)avatarUrl recipientId:(NSString *)recipientId
- (void)downloadAvatarForUserProfile:(UserProfile *)userProfile
{
OWSAssert(avatarUrl.length > 0);
OWSAssert(recipientId.length > 0);
OWSAssert([NSThread isMainThread]);
OWSAssert(userProfile);
// TODO:
if (userProfile.profileKey.length < 1 || userProfile.avatarUrl.length < 1) {
return;
}
NSData *profileKeyAtStart = userProfile.profileKey;
NSURL *url = [NSURL URLWithString:userProfile.avatarUrl];
if (!url) {
OWSFail(@"%@ Malformed avatar URL: %@", self.tag, userProfile.avatarUrl);
return;
}
NSString *_Nullable fileExtension = [[[url lastPathComponent] pathExtension] lowercaseString];
NSSet<NSString *> *validFileExtensions = [NSSet setWithArray:@[
@"jpg",
@"jpeg",
@"png",
@"gif",
]];
if (![validFileExtensions containsObject:fileExtension]) {
DDLogWarn(@"Ignoring avatar with invalid file extension: %@", userProfile.avatarUrl);
}
NSString *fileName = [[NSUUID UUID].UUIDString stringByAppendingPathExtension:fileExtension];
NSString *filePath = [self.profileAvatarsDirPath stringByAppendingPathComponent:fileName];
if ([self.currentAvatarDownloads containsObject:userProfile.recipientId]) {
// Download already in flight; ignore.
return;
}
[self.currentAvatarDownloads addObject:userProfile.recipientId];
NSString *tempDirectory = NSTemporaryDirectory();
NSString *tempFilePath = [tempDirectory stringByAppendingPathComponent:fileName];
// TODO: Should we use a special configuration as we do in TSNetworkManager?
// TODO: How does censorship circumvention fit in?
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request
progress:nil
destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
return [NSURL fileURLWithPath:tempFilePath];
}
completionHandler:^(NSURLResponse *response, NSURL *filePathParam, NSError *error) {
OWSAssert([[NSURL fileURLWithPath:tempFilePath] isEqual:filePathParam]);
// Ensure disk IO and decryption occurs off the main thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *_Nullable encryptedData = (error ? nil : [NSData dataWithContentsOfFile:tempFilePath]);
NSData *_Nullable decryptedData =
[OWSProfileManager decryptProfileData:encryptedData profileKey:profileKeyAtStart];
UIImage *_Nullable image = nil;
if (decryptedData) {
// TODO: Verify avatar digest.
BOOL success = [decryptedData writeToFile:filePath atomically:YES];
if (success) {
image = [UIImage imageWithContentsOfFile:filePath];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.currentAvatarDownloads removeObject:userProfile.recipientId];
UserProfile *currentUserProfile =
[self getOrBuildUserProfileForRecipientId:userProfile.recipientId];
if (currentUserProfile.profileKey.length < 1
|| ![currentUserProfile.profileKey isEqual:userProfile.profileKey]) {
DDLogWarn(@"%@ Ignoring avatar download for obsolete user profile.", self.tag);
} else if (error) {
DDLogError(@"%@ avatar download failed: %@", self.tag, error);
} else if (!encryptedData) {
DDLogError(@"%@ avatar encrypted data could not be read.", self.tag);
} else if (!decryptedData) {
DDLogError(@"%@ avatar data could not be decrypted.", self.tag);
} else if (!image) {
DDLogError(@"%@ avatar image could not be loaded: %@", self.tag, error);
} else {
[self.otherUsersProfileAvatarImageCache setObject:image forKey:userProfile.recipientId];
userProfile.avatarFileName = fileName;
[self saveUserProfile:userProfile];
}
});
});
}];
[downloadTask resume];
}
- (void)refreshProfileForRecipientId:(NSString *)recipientId
@ -721,7 +813,7 @@ static const NSInteger kProfileKeyLength = 16;
[self.otherUsersProfileAvatarImageCache removeObjectForKey:recipientId];
if (avatarUrl) {
[self downloadProfileAvatarWithUrl:avatarUrl recipientId:recipientId];
[self downloadAvatarForUserProfile:userProfile];
}
}

Loading…
Cancel
Save