Update profile on service.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 83d01eed76
commit 823927685d

@ -10,7 +10,7 @@
#import <SignalServiceKit/OWSMessageSender.h> #import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/SecurityUtils.h> #import <SignalServiceKit/SecurityUtils.h>
#import <SignalServiceKit/TSGroupThread.h> #import <SignalServiceKit/TSGroupThread.h>
#import <SignalServiceKit/TSStorageManager.h> #import <SignalServiceKit/TSSetProfileRequest.h>
#import <SignalServiceKit/TSStorageManager.h> #import <SignalServiceKit/TSStorageManager.h>
#import <SignalServiceKit/TSThread.h> #import <SignalServiceKit/TSThread.h>
#import <SignalServiceKit/TSYapDatabaseObject.h> #import <SignalServiceKit/TSYapDatabaseObject.h>
@ -423,17 +423,20 @@ static const NSInteger kProfileKeyLength = 16;
OWSAssert(failureBlock); OWSAssert(failureBlock);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// TODO: Do we need to use NSDataBase64EncodingOptions? NSData *_Nullable profileNameEncrypted = [self encryptProfileString:localProfileName];
NSString *_Nullable localProfileNameBase64 = [[self encryptProfileString:localProfileName] base64EncodedString];
NSString *_Nullable avatarUrlBase64 = [[avatarUrl dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
NSString *_Nullable avatarDigestBase64 = [avatarDigest base64EncodedString];
// TODO: TSSetProfileRequest *request = [[TSSetProfileRequest alloc] initWithProfileName:profileNameEncrypted
if (YES) { avatarUrl:avatarUrl
successBlock(); avatarDigest:avatarDigest];
return;
} [self.networkManager makeRequest:request
failureBlock(); success:^(NSURLSessionDataTask *task, id responseObject) {
successBlock();
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"%@ Failed to update profile with error: %@", self.tag, error);
failureBlock();
}];
}); });
} }
@ -669,46 +672,51 @@ static const NSInteger kProfileKeyLength = 16;
- (void)updateProfileForRecipientId:(NSString *)recipientId - (void)updateProfileForRecipientId:(NSString *)recipientId
profileNameEncrypted:(NSData *_Nullable)profileNameEncrypted profileNameEncrypted:(NSData *_Nullable)profileNameEncrypted
avatarUrlData:(NSData *_Nullable)avatarUrlData avatarUrlData:(NSData *_Nullable)avatarUrlData
avatarDigest:(NSData *_Nullable)avatarDigest avatarDigest:(NSData *_Nullable)avatarDigestParam
{ {
OWSAssert(recipientId.length > 0); OWSAssert(recipientId.length > 0);
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId]; // Ensure decryption, etc. off main thread.
if (!userProfile.profileKey) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
return;
}
NSString *_Nullable profileName = UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
[self decryptProfileString:profileNameEncrypted profileKey:userProfile.profileKey]; if (!userProfile.profileKey) {
NSString *_Nullable avatarUrl return;
= (avatarUrlData ? [[NSString alloc] initWithData:avatarUrlData encoding:NSUTF8StringEncoding] : nil); }
if (!avatarUrl || !avatarDigest) { NSString *_Nullable profileName =
// If either avatar url or digest is missing, skip both. [self decryptProfileString:profileNameEncrypted profileKey:userProfile.profileKey];
avatarUrl = nil; NSString *_Nullable avatarUrl
avatarDigest = nil; = (avatarUrlData ? [[NSString alloc] initWithData:avatarUrlData encoding:NSUTF8StringEncoding] : nil);
} NSData *_Nullable avatarDigest = avatarDigestParam;
BOOL isAvatarSame = ([self isNullableStringEqual:userProfile.avatarUrl toString:avatarUrl] && if (!avatarUrl || !avatarDigest) {
[self isNullableDataEqual:userProfile.avatarDigest toData:avatarDigest]); // If either avatar url or digest is missing, skip both.
avatarUrl = nil;
avatarDigest = nil;
}
dispatch_async(dispatch_get_main_queue(), ^{ BOOL isAvatarSame = ([self isNullableStringEqual:userProfile.avatarUrl toString:avatarUrl] &&
userProfile.profileName = profileName; [self isNullableDataEqual:userProfile.avatarDigest toData:avatarDigest]);
userProfile.avatarUrl = avatarUrl;
userProfile.avatarDigest = avatarDigest;
if (!isAvatarSame) { dispatch_async(dispatch_get_main_queue(), ^{
// Evacuate avatar image cache. userProfile.profileName = profileName;
[self.otherUsersProfileAvatarImageCache removeObjectForKey:recipientId]; userProfile.avatarUrl = avatarUrl;
userProfile.avatarDigest = avatarDigest;
if (!isAvatarSame) {
// Evacuate avatar image cache.
[self.otherUsersProfileAvatarImageCache removeObjectForKey:recipientId];
if (avatarUrl) { if (avatarUrl) {
[self downloadProfileAvatarWithUrl:avatarUrl recipientId:recipientId]; [self downloadProfileAvatarWithUrl:avatarUrl recipientId:recipientId];
}
} }
}
userProfile.lastUpdateDate = [NSDate new]; userProfile.lastUpdateDate = [NSDate new];
[self saveUserProfile:userProfile]; [self saveUserProfile:userProfile];
});
}); });
} }

@ -213,13 +213,15 @@ NS_ASSUME_NONNULL_BEGIN
{ {
__weak ProfileViewController *weakSelf = self; __weak ProfileViewController *weakSelf = self;
[OWSProfileManager.sharedManager updateLocalProfileName:self.nameTextField.text [OWSProfileManager.sharedManager updateLocalProfileName:self.nameTextField.text
avatarImage:self.avatar avatarImage:self.avatar
success:^{ success:^{
[weakSelf.navigationController popViewControllerAnimated:YES]; [weakSelf.navigationController popViewControllerAnimated:YES];
} }
failure:^{ failure:^{
// TODO: Handle failure. [OWSAlerts showAlertWithTitle:NSLocalizedString(@"ALERT_ERROR_TITLE", @"")
}]; message:NSLocalizedString(@"PROFILE_VIEW_ERROR_UPDATE_FAILED",
@"Error message shown when a profile update fails.")];
}];
} }
#pragma mark - UITextFieldDelegate #pragma mark - UITextFieldDelegate

@ -1069,6 +1069,9 @@
/* Label for action that clear's the user's profile avatar */ /* Label for action that clear's the user's profile avatar */
"PROFILE_VIEW_CLEAR_AVATAR" = "Clear Avatar"; "PROFILE_VIEW_CLEAR_AVATAR" = "Clear Avatar";
/* Error message shown when a profile update fails. */
"PROFILE_VIEW_ERROR_UPDATE_FAILED" = "Profile update failed.";
/* Default text for the profile name field of the profile view. */ /* Default text for the profile name field of the profile view. */
"PROFILE_VIEW_NAME_DEFAULT_TEXT" = "Enter your name."; "PROFILE_VIEW_NAME_DEFAULT_TEXT" = "Enter your name.";

@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
* Normally this is private, but we need to embed this * Normally this is private, but we need to embed this
* data structure within our own. * data structure within our own.
*/ */
- (OWSSignalServiceProtosDataMessage *)buildDataMessage:(NSString *)recipientId; - (OWSSignalServiceProtosDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId;
@end @end

@ -455,10 +455,9 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
return builder; return builder;
} }
- (OWSSignalServiceProtosDataMessage *)buildDataMessage:(NSString *)recipientId - (OWSSignalServiceProtosDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId
{ {
OWSAssert(self.thread); OWSAssert(self.thread);
OWSAssert(recipientId.length > 0);
OWSSignalServiceProtosDataMessageBuilder *builder = [self dataMessageBuilder]; OWSSignalServiceProtosDataMessageBuilder *builder = [self dataMessageBuilder];
[builder addLocalProfileKeyIfNecessary:self.thread recipientId:recipientId]; [builder addLocalProfileKeyIfNecessary:self.thread recipientId:recipientId];

@ -0,0 +1,19 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSSetProfileRequest : TSRequest
- (nullable instancetype)initWithProfileName:(NSData *_Nullable)profileNameEncrypted
avatarUrl:(NSString *_Nullable)avatarUrl
avatarDigest:(NSData *_Nullable)avatarDigest;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,37 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSSetProfileRequest.h"
#import "NSData+Base64.h"
#import "TSConstants.h"
NS_ASSUME_NONNULL_BEGIN
@implementation TSSetProfileRequest
- (nullable instancetype)initWithProfileName:(NSData *_Nullable)profileNameEncrypted
avatarUrl:(NSString *_Nullable)avatarUrl
avatarDigest:(NSData *_Nullable)avatarDigest
{
self = [super initWithURL:[NSURL URLWithString:textSecureSetProfileAPI]];
self.HTTPMethod = @"PUT";
if (profileNameEncrypted.length > 0) {
self.parameters[@"name"] = [profileNameEncrypted base64EncodedString];
}
if (avatarUrl.length > 0) {
self.parameters[@"avatar"] = [[avatarUrl dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
}
if (avatarDigest.length > 0) {
self.parameters[@"avatarDigest"] = [avatarDigest base64EncodedString];
}
return self;
}
@end
NS_ASSUME_NONNULL_END

@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSSignalServiceProtosDataMessageBuilder (OWS) @interface OWSSignalServiceProtosDataMessageBuilder (OWS)
- (void)addLocalProfileKeyIfNecessary:(TSThread *)thread recipientId:(NSString *)recipientId; - (void)addLocalProfileKeyIfNecessary:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId;
@end @end

@ -12,11 +12,9 @@ NS_ASSUME_NONNULL_BEGIN
@implementation PBGeneratedMessageBuilder (OWS) @implementation PBGeneratedMessageBuilder (OWS)
- (BOOL)shouldMessageHaveLocalProfileKey:(TSThread *)thread recipientId:(NSString *)recipientId - (BOOL)shouldMessageHaveLocalProfileKey:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId
// recipient:(SignalRecipient *)recipient
{ {
OWSAssert(thread); OWSAssert(thread);
OWSAssert(recipientId.length > 0);
id<ProfileManagerProtocol> profileManager = [TextSecureKitEnv sharedEnv].profileManager; id<ProfileManagerProtocol> profileManager = [TextSecureKitEnv sharedEnv].profileManager;
@ -25,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
// //
// For Group threads, we want to include the profile key IFF the // For Group threads, we want to include the profile key IFF the
// recipient OR the group is in the whitelist. // recipient OR the group is in the whitelist.
if ([profileManager isUserInProfileWhitelist:recipientId]) { if (recipientId.length > 0 && [profileManager isUserInProfileWhitelist:recipientId]) {
return YES; return YES;
} else if ([profileManager isThreadInProfileWhitelist:thread]) { } else if ([profileManager isThreadInProfileWhitelist:thread]) {
return YES; return YES;
@ -46,10 +44,9 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSSignalServiceProtosDataMessageBuilder (OWS) @implementation OWSSignalServiceProtosDataMessageBuilder (OWS)
- (void)addLocalProfileKeyIfNecessary:(TSThread *)thread recipientId:(NSString *)recipientId - (void)addLocalProfileKeyIfNecessary:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId
{ {
OWSAssert(thread); OWSAssert(thread);
OWSAssert(recipientId.length > 0);
if ([self shouldMessageHaveLocalProfileKey:thread recipientId:recipientId]) { if ([self shouldMessageHaveLocalProfileKey:thread recipientId:recipientId]) {
[self setProfileKey:self.localProfileKey]; [self setProfileKey:self.localProfileKey];

@ -41,6 +41,7 @@ typedef enum { kSMSVerification, kPhoneNumberVerification } VerificationTranspor
#define textSecureDeviceProvisioningAPIFormat @"v1/provisioning/%@" #define textSecureDeviceProvisioningAPIFormat @"v1/provisioning/%@"
#define textSecureDevicesAPIFormat @"v1/devices/%@" #define textSecureDevicesAPIFormat @"v1/devices/%@"
#define textSecureProfileAPIFormat @"v1/profile/%@" #define textSecureProfileAPIFormat @"v1/profile/%@"
#define textSecureSetProfileAPI @"v1/profile"
#pragma mark Push RegistrationSpecific Constants #pragma mark Push RegistrationSpecific Constants
typedef NS_ENUM(NSInteger, TSPushRegistrationError) { typedef NS_ENUM(NSInteger, TSPushRegistrationError) {

Loading…
Cancel
Save