Update profile on service.

// FREEBIE
This commit is contained in:
Matthew Chen 2017-08-04 12:46:16 -04:00
parent 83d01eed76
commit 823927685d
10 changed files with 127 additions and 61 deletions

View File

@ -10,7 +10,7 @@
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/SecurityUtils.h>
#import <SignalServiceKit/TSGroupThread.h>
#import <SignalServiceKit/TSStorageManager.h>
#import <SignalServiceKit/TSSetProfileRequest.h>
#import <SignalServiceKit/TSStorageManager.h>
#import <SignalServiceKit/TSThread.h>
#import <SignalServiceKit/TSYapDatabaseObject.h>
@ -423,17 +423,20 @@ 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 localProfileNameBase64 = [[self encryptProfileString:localProfileName] base64EncodedString];
NSString *_Nullable avatarUrlBase64 = [[avatarUrl dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
NSString *_Nullable avatarDigestBase64 = [avatarDigest base64EncodedString];
NSData *_Nullable profileNameEncrypted = [self encryptProfileString:localProfileName];
// TODO:
if (YES) {
successBlock();
return;
}
failureBlock();
TSSetProfileRequest *request = [[TSSetProfileRequest alloc] initWithProfileName:profileNameEncrypted
avatarUrl:avatarUrl
avatarDigest:avatarDigest];
[self.networkManager makeRequest:request
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
profileNameEncrypted:(NSData *_Nullable)profileNameEncrypted
avatarUrlData:(NSData *_Nullable)avatarUrlData
avatarDigest:(NSData *_Nullable)avatarDigest
avatarDigest:(NSData *_Nullable)avatarDigestParam
{
OWSAssert(recipientId.length > 0);
UserProfile *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
if (!userProfile.profileKey) {
return;
}
// Ensure decryption, etc. off main thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *_Nullable profileName =
[self decryptProfileString:profileNameEncrypted profileKey:userProfile.profileKey];
NSString *_Nullable avatarUrl
= (avatarUrlData ? [[NSString alloc] initWithData:avatarUrlData encoding:NSUTF8StringEncoding] : nil);
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 *userProfile = [self getOrBuildUserProfileForRecipientId:recipientId];
if (!userProfile.profileKey) {
return;
}
userProfile.lastUpdateDate = [NSDate new];
NSString *_Nullable profileName =
[self decryptProfileString:profileNameEncrypted profileKey:userProfile.profileKey];
NSString *_Nullable avatarUrl
= (avatarUrlData ? [[NSString alloc] initWithData:avatarUrlData encoding:NSUTF8StringEncoding] : nil);
NSData *_Nullable avatarDigest = avatarDigestParam;
[self saveUserProfile:userProfile];
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];
});
});
}

View File

@ -213,13 +213,15 @@ NS_ASSUME_NONNULL_BEGIN
{
__weak ProfileViewController *weakSelf = self;
[OWSProfileManager.sharedManager updateLocalProfileName:self.nameTextField.text
avatarImage:self.avatar
success:^{
[weakSelf.navigationController popViewControllerAnimated:YES];
}
failure:^{
// TODO: Handle failure.
}];
avatarImage:self.avatar
success:^{
[weakSelf.navigationController popViewControllerAnimated:YES];
}
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

View File

@ -1069,6 +1069,9 @@
/* Label for action that clear's the user's profile 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. */
"PROFILE_VIEW_NAME_DEFAULT_TEXT" = "Enter your name.";

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -12,11 +12,9 @@ NS_ASSUME_NONNULL_BEGIN
@implementation PBGeneratedMessageBuilder (OWS)
- (BOOL)shouldMessageHaveLocalProfileKey:(TSThread *)thread recipientId:(NSString *)recipientId
// recipient:(SignalRecipient *)recipient
- (BOOL)shouldMessageHaveLocalProfileKey:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId
{
OWSAssert(thread);
OWSAssert(recipientId.length > 0);
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
// recipient OR the group is in the whitelist.
if ([profileManager isUserInProfileWhitelist:recipientId]) {
if (recipientId.length > 0 && [profileManager isUserInProfileWhitelist:recipientId]) {
return YES;
} else if ([profileManager isThreadInProfileWhitelist:thread]) {
return YES;
@ -46,10 +44,9 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSSignalServiceProtosDataMessageBuilder (OWS)
- (void)addLocalProfileKeyIfNecessary:(TSThread *)thread recipientId:(NSString *)recipientId
- (void)addLocalProfileKeyIfNecessary:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId
{
OWSAssert(thread);
OWSAssert(recipientId.length > 0);
if ([self shouldMessageHaveLocalProfileKey:thread recipientId:recipientId]) {
[self setProfileKey:self.localProfileKey];

View File

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