From bf54d78b9919cbe5d5b5bc50d46525f90d0fdb46 Mon Sep 17 00:00:00 2001 From: Niels Andriesse <andriesseniels@gmail.com> Date: Thu, 20 Feb 2020 16:59:05 +1100 Subject: [PATCH] Untie profile picture from auth token --- Signal/Signal-Info.plist | 2 +- Signal/src/AppDelegate.m | 21 +++++++- SignalMessaging/profiles/OWSProfileManager.m | 6 +-- .../src/Loki/API/LokiFileServerAPI.swift | 53 ++++++++----------- .../src/Loki/Utilities/LKUserDefaults.swift | 11 +++- 5 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 342d37adb..378a5f3ca 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -5,7 +5,7 @@ <key>BuildDetails</key> <dict> <key>CarthageVersion</key> - <string>0.34.0</string> + <string>0.33.0</string> <key>OSXVersion</key> <string>10.15.3</string> <key>WebRTCCommit</key> diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 3039232bf..c3e764c2a 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -770,6 +770,8 @@ static NSTimeInterval launchStartedAt; [self.socketManager requestSocketOpen]; [Environment.shared.contactsManager fetchSystemContactsOnceIfAlreadyAuthorized]; + NSString *userHexEncodedPublicKey = self.tsAccountManager.localNumber; + // Loki: Tell our friends that we are online [LKP2PAPI broadcastOnlineStatus]; @@ -777,7 +779,22 @@ static NSTimeInterval launchStartedAt; [self startLongPollerIfNeeded]; // Loki: Get device links - [LKFileServerAPI getDeviceLinksAssociatedWith:self.tsAccountManager.localNumber]; + [[LKFileServerAPI getDeviceLinksAssociatedWith:userHexEncodedPublicKey] retainUntilComplete]; + + // Loki: Update profile picture if needed + NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; + NSDate *now = [NSDate new]; + NSDate *lastProfilePictureUpload = (NSDate *)[userDefaults objectForKey:@"lastProfilePictureUpload"]; + if ([now timeIntervalSinceDate:lastProfilePictureUpload] > 14 * 24 * 60 * 60) { + OWSProfileManager *profileManager = OWSProfileManager.sharedManager; + NSString *displayName = [profileManager profileNameForRecipientId:userHexEncodedPublicKey]; + UIImage *profilePicture = [profileManager profileAvatarForRecipientId:userHexEncodedPublicKey]; + [profileManager updateLocalProfileName:displayName avatarImage:profilePicture success:^{ + [userDefaults setObject:now forKey:@"lastProfilePictureUpload"]; + } failure:^(NSError *error) { + // Do nothing + } requiresSync:YES]; + } if (![UIApplication sharedApplication].isRegisteredForRemoteNotifications) { OWSLogInfo(@"Retrying to register for remote notifications since user hasn't registered yet."); @@ -1448,7 +1465,7 @@ static NSTimeInterval launchStartedAt; [self startLongPollerIfNeeded]; // Loki: Get device links - [LKFileServerAPI getDeviceLinksAssociatedWith:self.tsAccountManager.localNumber]; + [[LKFileServerAPI getDeviceLinksAssociatedWith:self.tsAccountManager.localNumber] retainUntilComplete]; // TODO: Is this even needed? } } diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index bd2b6887e..1bbea7d6e 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -416,10 +416,10 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); NSData *encryptedAvatarData = [self encryptProfileData:avatarData profileKey:newProfileKey]; OWSAssertDebug(encryptedAvatarData.length > 0); - [[LKFileServerAPI setProfilePicture:encryptedAvatarData] - .thenOn(dispatch_get_main_queue(), ^(NSString *url) { + [[LKFileServerAPI uploadProfilePicture:encryptedAvatarData] + .thenOn(dispatch_get_main_queue(), ^(NSString *downloadURL) { [self.localUserProfile updateWithProfileKey:newProfileKey dbConnection:self.dbConnection completion:^{ - successBlock(url); + successBlock(downloadURL); }]; }) .catchOn(dispatch_get_main_queue(), ^(id result) { diff --git a/SignalServiceKit/src/Loki/API/LokiFileServerAPI.swift b/SignalServiceKit/src/Loki/API/LokiFileServerAPI.swift index 7dcac5e9f..9bde68e93 100644 --- a/SignalServiceKit/src/Loki/API/LokiFileServerAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiFileServerAPI.swift @@ -137,40 +137,33 @@ public final class LokiFileServerAPI : LokiDotNetAPI { } // MARK: Profile Pictures (Public API) - public static func setProfilePicture(_ profilePicture: Data) -> Promise<String> { - return Promise<String>() { seal in - guard profilePicture.count < maxFileSize else { return seal.reject(LokiDotNetAPIError.maxFileSizeExceeded) } - getAuthToken(for: server).done { token in - let url = "\(server)/users/me/avatar" - let parameters: JSON = [ "type" : attachmentType, "Content-Type" : "application/binary" ] - var error: NSError? - var request = AFHTTPRequestSerializer().multipartFormRequest(withMethod: "POST", urlString: url, parameters: parameters, constructingBodyWith: { formData in - formData.appendPart(withFileData: profilePicture, name: "avatar", fileName: UUID().uuidString, mimeType: "application/binary") - }, error: &error) - request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") - if let error = error { - print("[Loki] Couldn't upload profile picture due to error: \(error).") - throw error - } - let _ = LokiFileServerProxy(for: server).performLokiFileServerNSURLRequest(request as NSURLRequest).done { responseObject in - guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let profilePicture = data["avatar_image"] as? JSON, let downloadURL = profilePicture["url"] as? String else { - print("[Loki] Couldn't parse profile picture from: \(responseObject).") - return seal.reject(LokiDotNetAPIError.parsingFailed) - } - return seal.fulfill(downloadURL) - }.catch { error in - seal.reject(error) - } - }.catch { error in - print("[Loki] Couldn't upload profile picture due to error: \(error).") - seal.reject(error) + public static func uploadProfilePicture(_ profilePicture: Data) -> Promise<String> { + guard profilePicture.count < maxFileSize else { return Promise(error: LokiDotNetAPIError.maxFileSizeExceeded) } + let url = "\(server)/files" + let parameters: JSON = [ "type" : attachmentType, "Content-Type" : "application/binary" ] + var error: NSError? + var request = AFHTTPRequestSerializer().multipartFormRequest(withMethod: "POST", urlString: url, parameters: parameters, constructingBodyWith: { formData in + formData.appendPart(withFileData: profilePicture, name: "content", fileName: UUID().uuidString, mimeType: "application/binary") + }, error: &error) + // Uploads to the Loki File Server shouldn't include any personally identifiable information so use a dummy auth token + request.addValue("Bearer loki", forHTTPHeaderField: "Authorization") + if let error = error { + print("[Loki] Couldn't upload profile picture due to error: \(error).") + return Promise(error: error) + } + return LokiFileServerProxy(for: server).performLokiFileServerNSURLRequest(request as NSURLRequest).map { responseObject in + guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let downloadURL = data["url"] as? String else { + print("[Loki] Couldn't parse profile picture from: \(responseObject).") + throw LokiDotNetAPIError.parsingFailed } + UserDefaults.standard[.lastProfilePictureUpload] = Date().timeIntervalSince1970 + return downloadURL } } // MARK: Profile Pictures (Public Obj-C API) - @objc(setProfilePicture:) - public static func objc_setProfilePicture(_ profilePicture: Data) -> AnyPromise { - return AnyPromise.from(setProfilePicture(profilePicture)) + @objc(uploadProfilePicture:) + public static func objc_uploadProfilePicture(_ profilePicture: Data) -> AnyPromise { + return AnyPromise.from(uploadProfilePicture(profilePicture)) } } diff --git a/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift b/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift index f1d426fd7..84921c642 100644 --- a/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift +++ b/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift @@ -9,7 +9,11 @@ public enum LKUserDefaults { /// Whether the device was unlinked as a slave device (used to notify the user on the landing screen). case wasUnlinked } - + + public enum Date : Swift.String { + case lastProfilePictureUpload + } + public enum Double : Swift.String { case lastDeviceTokenUpload = "lastDeviceTokenUploadTime" } @@ -36,6 +40,11 @@ public extension UserDefaults { get { return self.bool(forKey: bool.rawValue) } set { set(newValue, forKey: bool.rawValue) } } + + public subscript(date: LKUserDefaults.Date) -> Date? { + get { return self.object(forKey: date.rawValue) as? Date } + set { set(newValue, forKey: date.rawValue) } + } public subscript(double: LKUserDefaults.Double) -> Double { get { return self.double(forKey: double.rawValue) }