diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 907f97370..e85eae446 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -405,150 +405,36 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); OWSAssertDebug(successBlock); OWSAssertDebug(failureBlock); OWSAssertDebug(avatarData == nil || avatarData.length > 0); - - [[LKStorageAPI setProfilePicture:avatarData] - .thenOn(dispatch_get_main_queue(), ^(NSString *url) { - successBlock(url); - }) - .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]) { - successBlock(result); - } else { - failureBlock(result); - } - }) retainUntilComplete]; - /* - // We want to clear the local user's profile avatar as soon as - // we request the upload form, since that request clears our - // avatar on the service. - // - // TODO: Revisit this so that failed profile updates don't leave - // the profile avatar blank, etc. - void (^clearLocalAvatar)(void) = ^{ - OWSUserProfile *userProfile = self.localUserProfile; - [userProfile updateWithAvatarUrlPath:nil avatarFileName:nil dbConnection:self.dbConnection completion:nil]; - }; - 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]; + NSData *_Nullable encryptedAvatarData; + if (avatarData) { + encryptedAvatarData = [self encryptProfileData:avatarData profileKey:newProfileKey]; + OWSAssertDebug(encryptedAvatarData.length > 0); + } - // See: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html - TSRequest *formRequest = [OWSRequestFactory profileAvatarUploadFormRequest]; - - [self.networkManager makeRequest:formRequest - success:^(NSURLSessionDataTask *task, id formResponseObject) { - if (avatarData == nil) { - OWSLogDebug(@"successfully cleared avatar"); - clearLocalAvatar(); - successBlock(nil); - return; - } - - if (![formResponseObject isKindOfClass:[NSDictionary class]]) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidResponse]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSDictionary *responseMap = formResponseObject; - OWSLogError(@"responseObject: %@", formResponseObject); - - NSString *formAcl = responseMap[@"acl"]; - if (![formAcl isKindOfClass:[NSString class]] || formAcl.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidAcl]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSString *formKey = responseMap[@"key"]; - if (![formKey isKindOfClass:[NSString class]] || formKey.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidKey]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSString *formPolicy = responseMap[@"policy"]; - if (![formPolicy isKindOfClass:[NSString class]] || formPolicy.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidPolicy]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSString *formAlgorithm = responseMap[@"algorithm"]; - if (![formAlgorithm isKindOfClass:[NSString class]] || formAlgorithm.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidAlgorithm]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSString *formCredential = responseMap[@"credential"]; - if (![formCredential isKindOfClass:[NSString class]] || formCredential.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidCredential]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSString *formDate = responseMap[@"date"]; - if (![formDate isKindOfClass:[NSString class]] || formDate.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidDate]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - NSString *formSignature = responseMap[@"signature"]; - if (![formSignature isKindOfClass:[NSString class]] || formSignature.length < 1) { - OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidSignature]); - return failureBlock( - OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed.")); - } - - [self.avatarHTTPManager POST:@"" - parameters:nil - constructingBodyWithBlock:^(id _Nonnull formData) { - NSData * (^formDataForString)(NSString *formString) = ^(NSString *formString) { - return [formString dataUsingEncoding:NSUTF8StringEncoding]; - }; - - // We have to build up the form manually vs. simply passing in a paramaters dict - // because AWS is sensitive to the order of the form params (at least the "key" - // field must occur early on). - // For consistency, all fields are ordered here in a known working order. - [formData appendPartWithFormData:formDataForString(formKey) name:@"key"]; - [formData appendPartWithFormData:formDataForString(formAcl) name:@"acl"]; - [formData appendPartWithFormData:formDataForString(formAlgorithm) name:@"x-amz-algorithm"]; - [formData appendPartWithFormData:formDataForString(formCredential) name:@"x-amz-credential"]; - [formData appendPartWithFormData:formDataForString(formDate) name:@"x-amz-date"]; - [formData appendPartWithFormData:formDataForString(formPolicy) name:@"policy"]; - [formData appendPartWithFormData:formDataForString(formSignature) name:@"x-amz-signature"]; - [formData appendPartWithFormData:formDataForString(OWSMimeTypeApplicationOctetStream) - name:@"Content-Type"]; - NSData *encryptedAvatarData = [self encryptProfileData:avatarData]; - OWSAssertDebug(encryptedAvatarData.length > 0); - [formData appendPartWithFormData:encryptedAvatarData name:@"file"]; - - OWSLogVerbose(@"constructed body"); - } - progress:^(NSProgress *_Nonnull uploadProgress) { - OWSLogVerbose(@"avatar upload progress: %.2f%%", uploadProgress.fractionCompleted * 100); - } - success:^(NSURLSessionDataTask *_Nonnull uploadTask, id _Nullable responseObject) { - OWSLogInfo(@"successfully uploaded avatar with key: %@", formKey); - successBlock(formKey); - } - failure:^(NSURLSessionDataTask *_Nullable uploadTask, NSError *error) { - OWSLogError(@"uploading avatar failed with error: %@", error); - clearLocalAvatar(); - return failureBlock(error); - }]; - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - // Only clear the local avatar if we have a response. Otherwise, we - // had a network failure and probably didn't reach the service. - if (task.response != nil) { - clearLocalAvatar(); - } - - OWSLogError(@"Failed to get profile avatar upload form: %@", error); - return failureBlock(error); + [[LKStorageAPI setProfilePicture:encryptedAvatarData] + .thenOn(dispatch_get_main_queue(), ^(NSString *url) { + [self.localUserProfile updateWithProfileKey:newProfileKey dbConnection:self.dbConnection completion:^{ + successBlock(url); }]; + }) + .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]) { + [self.localUserProfile updateWithProfileKey:newProfileKey dbConnection:self.dbConnection completion:^{ + successBlock(result); + }]; + } else { + failureBlock(result); + } + }) retainUntilComplete]; }); - */ } - (void)updateServiceWithProfileName:(nullable NSString *)localProfileName