diff --git a/SignalMessaging/profiles/ProfileFetcherJob.swift b/SignalMessaging/profiles/ProfileFetcherJob.swift index 068465d01..c06f96090 100644 --- a/SignalMessaging/profiles/ProfileFetcherJob.swift +++ b/SignalMessaging/profiles/ProfileFetcherJob.swift @@ -125,7 +125,7 @@ public class ProfileFetcherJob: NSObject { reject(error) } }, - failure: { (_: NSInteger, error: Error) in + failure: { (_: NSInteger, _:Data?, error: Error) in reject(error) }) } else { diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index 0fa86bca4..1972c4b74 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -35,7 +35,12 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling @interface TSAccountManager () @property (nonatomic, readonly) BOOL isRegistered; + +// This property is exposed publicly for testing purposes only. +#ifndef DEBUG @property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification; +#endif + @property (nonatomic, nullable) NSString *cachedLocalNumber; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index d50c1b431..68bebe27e 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -985,7 +985,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; deviceMessages:deviceMessages success:successHandler]; } - failure:^(NSInteger statusCode, NSError *error) { + failure:^(NSInteger statusCode, NSData *_Nullable responseData, NSError *error) { [self messageSendDidFail:message recipient:recipient thread:thread @@ -994,6 +994,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; remainingAttempts:remainingAttempts statusCode:statusCode error:error + responseData:responseData success:successHandler failure:failureHandler]; }]; @@ -1009,6 +1010,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; failure:^(NSURLSessionDataTask *task, NSError *error) { NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; NSInteger statusCode = response.statusCode; + NSData *_Nullable responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; [self messageSendDidFail:message recipient:recipient @@ -1018,6 +1020,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; remainingAttempts:remainingAttempts statusCode:statusCode error:error + responseData:responseData success:successHandler failure:failureHandler]; }]; @@ -1067,7 +1070,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; deviceMessages:(NSArray *)deviceMessages remainingAttempts:(int)remainingAttempts statusCode:(NSInteger)statusCode - error:(NSError *)error + error:(NSError *)responseError + responseData:(nullable NSData *)responseData success:(void (^)(void))successHandler failure:(RetryableFailureHandler)failureHandler { @@ -1075,21 +1079,18 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; OWSAssert(recipient); OWSAssert(thread); OWSAssert(deviceMessages); - OWSAssert(error); + OWSAssert(responseError); OWSAssert(successHandler); OWSAssert(failureHandler); DDLogInfo(@"%@ sending to recipient: %@, failed with error.", self.logTag, recipient.uniqueId); - [DDLog flushLog]; - - NSData *responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; void (^retrySend)(void) = ^void() { if (remainingAttempts <= 0) { // Since we've already repeatedly failed to send to the messaging API, // it's unlikely that repeating the whole process will succeed. - [error setIsRetryable:NO]; - return failureHandler(error); + [responseError setIsRetryable:NO]; + return failureHandler(responseError); } dispatch_async([OWSDispatch sendingQueue], ^{ @@ -1130,32 +1131,39 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } case 409: { // Mismatched devices - DDLogWarn(@"%@ Mismatch Devices for recipient: %@", self.logTag, recipient.uniqueId); + DDLogWarn(@"%@ Mismatched devices for recipient: %@", self.logTag, recipient.uniqueId); - NSError *error; - NSDictionary *serializedResponse = - [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; - if (error) { + NSError *_Nullable error = nil; + NSDictionary *_Nullable responseJson = nil; + if (responseData) { + responseJson = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; + } + if (error || !responseJson) { OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotParseMismatchedDevicesJson]); [error setIsRetryable:YES]; return failureHandler(error); } - [self handleMismatchedDevices:serializedResponse recipient:recipient completion:retrySend]; + [self handleMismatchedDevicesWithResponseJson:responseJson recipient:recipient completion:retrySend]; break; } case 410: { // Stale devices DDLogWarn(@"%@ Stale devices for recipient: %@", self.logTag, recipient.uniqueId); - if (!responseData) { + NSError *_Nullable error = nil; + NSDictionary *_Nullable responseJson = nil; + if (responseData) { + responseJson = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; + } + if (error || !responseJson) { DDLogWarn(@"Stale devices but server didn't specify devices in response."); NSError *error = OWSErrorMakeUnableToProcessServerResponseError(); [error setIsRetryable:YES]; return failureHandler(error); } - [self handleStaleDevicesWithResponse:responseData recipientId:recipient.uniqueId completion:retrySend]; + [self handleStaleDevicesWithResponseJson:responseJson recipientId:recipient.uniqueId completion:retrySend]; break; } default: @@ -1164,12 +1172,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } } -- (void)handleMismatchedDevices:(NSDictionary *)dictionary - recipient:(SignalRecipient *)recipient - completion:(void (^)(void))completionHandler +- (void)handleMismatchedDevicesWithResponseJson:(NSDictionary *)responseJson + recipient:(SignalRecipient *)recipient + completion:(void (^)(void))completionHandler { - NSArray *extraDevices = [dictionary objectForKey:@"extraDevices"]; - NSArray *missingDevices = [dictionary objectForKey:@"missingDevices"]; + OWSAssert(responseJson); + OWSAssert(recipient); + OWSAssert(completionHandler); + + NSArray *extraDevices = responseJson[@"extraDevices"]; + NSArray *missingDevices = responseJson[@"missingDevices"]; if (missingDevices.count > 0) { NSString *localNumber = [TSAccountManager localNumber]; @@ -1442,13 +1454,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } // Called when the server indicates that the devices no longer exist - e.g. when the remote recipient has reinstalled. -- (void)handleStaleDevicesWithResponse:(NSData *)responseData - recipientId:(NSString *)identifier - completion:(void (^)(void))completionHandler +- (void)handleStaleDevicesWithResponseJson:(NSDictionary *)responseJson + recipientId:(NSString *)identifier + completion:(void (^)(void))completionHandler { dispatch_async([OWSDispatch sendingQueue], ^{ - NSDictionary *serialization = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil]; - NSArray *devices = serialization[@"staleDevices"]; + NSArray *devices = responseJson[@"staleDevices"]; if (!([devices count] > 0)) { return; diff --git a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.h b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.h index acc1c61b2..80a72f7fd 100644 --- a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.h +++ b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.h @@ -18,7 +18,7 @@ typedef NS_ENUM(NSUInteger, SocketManagerState) { typedef void (^TSSocketMessageSuccess)(id _Nullable responseObject); // statusCode is zero by default, if request never made or failed. -typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSError *error); +typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSData *_Nullable responseData, NSError *error); @class TSRequest; diff --git a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m index 324f28316..3d8a17a27 100644 --- a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m +++ b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m @@ -102,10 +102,10 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageRequestFailed, NSLocalizedString(@"ERROR_DESCRIPTION_REQUEST_FAILED", @"Error indicating that a socket request failed.")); - [self didFailWithStatusCode:0 error:error]; + [self didFailWithStatusCode:0 responseData:nil error:error]; } -- (void)didFailWithStatusCode:(NSInteger)statusCode error:(NSError *)error +- (void)didFailWithStatusCode:(NSInteger)statusCode responseData:(nullable NSData *)responseData error:(NSError *)error { OWSAssert(error); @@ -117,12 +117,14 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ self.hasCompleted = YES; } + DDLogError(@"%@ %s didFailWithStatusCode: %zd, %@", self.logTag, __PRETTY_FUNCTION__, statusCode, error); + OWSAssert(self.success); OWSAssert(self.failure); TSSocketMessageFailure failure = self.failure; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - failure(statusCode, error); + failure(statusCode, responseData, error); }); self.success = nil; @@ -568,18 +570,18 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ if (message.hasMessage) { responseMessage = message.message; } - NSData *_Nullable responseBody; + NSData *_Nullable responseData; if (message.hasBody) { - responseBody = message.body; + responseData = message.body; } NSArray *_Nullable responseHeaders = message.headers; BOOL hasValidResponse = YES; - id responseObject = responseBody; - if (responseBody) { + id responseObject = responseData; + if (responseData) { NSError *error; id _Nullable responseJson = - [NSJSONSerialization JSONObjectWithData:responseBody options:(NSJSONReadingOptions)0 error:&error]; + [NSJSONSerialization JSONObjectWithData:responseData options:(NSJSONReadingOptions)0 error:&error]; if (!responseJson || error) { OWSProdLogAndFail(@"%@ could not parse WebSocket response JSON: %@.", self.logTag, error); hasValidResponse = NO; @@ -606,7 +608,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageResponseFailed, NSLocalizedString( @"ERROR_DESCRIPTION_RESPONSE_FAILED", @"Error indicating that a socket response failed.")); - [socketMessage didFailWithStatusCode:(NSInteger)responseStatus error:error]; + [socketMessage didFailWithStatusCode:(NSInteger)responseStatus responseData:responseData error:error]; } } @@ -615,7 +617,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ (unsigned long long)requestId, (NSInteger)responseStatus, responseMessage, - responseBody.length, + responseData.length, responseHeaders, socketMessage != nil, responseObject);