Merge branch 'charlesmchen/analytics3'

pull/1/head
Matthew Chen 8 years ago
commit 9962c936c6

@ -125,7 +125,7 @@ EXTERNAL SOURCES:
:branch: signal-master :branch: signal-master
:git: https://github.com/WhisperSystems/JSQMessagesViewController.git :git: https://github.com/WhisperSystems/JSQMessagesViewController.git
SignalServiceKit: SignalServiceKit:
:path: "." :path: .
SocketRocket: SocketRocket:
:git: https://github.com/facebook/SocketRocket.git :git: https://github.com/facebook/SocketRocket.git

@ -263,7 +263,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{ {
DDLogError(@"%@ Failed to register for remote notifications with error %@", self.tag, error); OWSProdErrorWNSError(@"app_delegate_error_failed_to_register_for_remote_notifications", error);
#ifdef DEBUG #ifdef DEBUG
DDLogWarn(@"%@ We're in debug mode. Faking success for remote registration with a fake push identifier", self.tag); DDLogWarn(@"%@ We're in debug mode. Faking success for remote registration with a fake push identifier", self.tag);
[PushManager.sharedManager.pushNotificationFutureSource trySetResult:[[NSMutableData dataWithLength:32] copy]]; [PushManager.sharedManager.pushNotificationFutureSource trySetResult:[[NSMutableData dataWithLength:32] copy]];
@ -334,25 +334,24 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
NSError *typeError; NSError *typeError;
[url getResourceValue:&utiType forKey:NSURLTypeIdentifierKey error:&typeError]; [url getResourceValue:&utiType forKey:NSURLTypeIdentifierKey error:&typeError];
if (typeError) { if (typeError) {
DDLogError( OWSFail(
@"%@ Determining type of picked document at url: %@ failed with error: %@", self.tag, url, typeError); @"%@ Determining type of picked document at url: %@ failed with error: %@", self.tag, url, typeError);
OWSAssert(NO); return NO;
} }
if (!utiType) { if (!utiType) {
DDLogDebug(@"%@ falling back to default filetype for picked document at url: %@", self.tag, url); OWSFail(@"%@ falling back to default filetype for picked document at url: %@", self.tag, url);
OWSAssert(NO);
utiType = (__bridge NSString *)kUTTypeData; utiType = (__bridge NSString *)kUTTypeData;
return NO;
} }
NSNumber *isDirectory; NSNumber *isDirectory;
NSError *isDirectoryError; NSError *isDirectoryError;
[url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&isDirectoryError]; [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&isDirectoryError];
if (isDirectoryError) { if (isDirectoryError) {
DDLogError(@"%@ Determining if picked document at url: %@ was a directory failed with error: %@", OWSFail(@"%@ Determining if picked document at url: %@ was a directory failed with error: %@",
self.tag, self.tag,
url, url,
isDirectoryError); isDirectoryError);
OWSAssert(NO);
return NO; return NO;
} else if ([isDirectory boolValue]) { } else if ([isDirectory boolValue]) {
DDLogInfo(@"%@ User picked directory at url: %@", self.tag, url); DDLogInfo(@"%@ User picked directory at url: %@", self.tag, url);

@ -34,6 +34,10 @@ static NSString *const OWS103EnableVideoCallingMigrationId = @"103";
[self save]; [self save];
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(
@"error_enable_video_calling_request_failed", error);
}
DDLogError(@"%@ failed with error: %@", self.tag, error); DDLogError(@"%@ failed with error: %@", self.tag, error);
}]; }];
}]; }];

@ -115,23 +115,7 @@
return [thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending; return [thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending;
} }
#pragma mark Upgrading to 2.1 - Needs to register VOIP token + Removing video cache folder #pragma mark Upgrading to 2.1 - Removing video cache folder
+ (void)nonBlockingPushRegistration {
void (^failedBlock)(NSError *) = ^(NSError *error) {
DDLogError(@"Failed to register VOIP push token: %@", error.debugDescription);
};
[[PushManager sharedManager] requestPushTokenWithSuccess:^(NSString *pushToken, NSString *voipToken) {
[[TSAccountManager sharedInstance]
registerForPushNotificationsWithPushToken:pushToken
voipToken:voipToken
success:^{
DDLogWarn(@"Registered for VOIP Push.");
}
failure:failedBlock];
}
failure:failedBlock];
}
+ (void)clearVideoCache { + (void)clearVideoCache {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
@ -162,13 +146,16 @@
TSUpdateAttributesRequest *request = [[TSUpdateAttributesRequest alloc] initWithUpdatedAttributesWithVoice]; TSUpdateAttributesRequest *request = [[TSUpdateAttributesRequest alloc] initWithUpdatedAttributesWithVoice];
[[TSNetworkManager sharedManager] makeRequest:request [[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) { success:^(NSURLSessionDataTask *task, id responseObject) {
success = YES; success = YES;
dispatch_semaphore_signal(sema); dispatch_semaphore_signal(sema);
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
success = NO; if (!IsNSErrorNetworkFailure(error)) {
DDLogError(@"Updating attributess failed with error: %@", error.description); OWSProdErrorWNSError(@"error_update_attributes_request_failed", error);
dispatch_semaphore_signal(sema); }
success = NO;
DDLogError(@"Updating attributess failed with error: %@", error.description);
dispatch_semaphore_signal(sema);
}]; }];

@ -159,6 +159,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
failure:failureHandler failure:failureHandler
remainingRetries:remainingRetries - 1]; remainingRetries:remainingRetries - 1];
} else { } else {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"accounts_error_register_push_tokens_failed", error);
}
failureHandler(error); failureHandler(error);
} }
}]; }];
@ -193,6 +196,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
successBlock(); successBlock();
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"accounts_error_verification_code_request_failed", error);
}
DDLogError(@"%@ Failed to request verification code request with error:%@", self.tag, error); DDLogError(@"%@ Failed to request verification code request with error:%@", self.tag, error);
failureBlock(error); failureBlock(error);
}]; }];
@ -263,6 +269,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
} }
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"accounts_error_verify_account_request_failed", error);
}
DDLogWarn(@"%@ Error verifying code: %@", self.tag, error.debugDescription); DDLogWarn(@"%@ Error verifying code: %@", self.tag, error.debugDescription);
switch (error.code) { switch (error.code) {
case 403: { case 403: {
@ -316,6 +325,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
userInfo:nil]; userInfo:nil];
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"accounts_error_unregister_account_request_failed", error);
}
DDLogError(@"%@ Failed to unregister with error: %@", self.tag, error); DDLogError(@"%@ Failed to unregister with error: %@", self.tag, error);
failureBlock(error); failureBlock(error);
}]; }];

@ -181,10 +181,12 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 *
[TSPreKeyManager clearPreKeyUpdateFailureCount]; [TSPreKeyManager clearPreKeyUpdateFailureCount];
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) { if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_prekeys_update_failed_signed_and_onetime", error); if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) {
} else { OWSProdErrorWNSError(@"error_prekeys_update_failed_signed_and_onetime", error);
OWSProdErrorWNSError(@"error_prekeys_update_failed_just_signed", error); } else {
OWSProdErrorWNSError(@"error_prekeys_update_failed_just_signed", error);
}
} }
// Mark the prekeys as _NOT_ checked on failure. // Mark the prekeys as _NOT_ checked on failure.
@ -302,6 +304,9 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 *
} }
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_prekeys_current_signed_prekey_request_failed", error);
}
DDLogWarn(@"%@ Could not retrieve current signed key from the service.", self.tag); DDLogWarn(@"%@ Could not retrieve current signed key from the service.", self.tag);
// Mark the prekeys as _NOT_ checked on failure. // Mark the prekeys as _NOT_ checked on failure.
@ -310,6 +315,9 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 *
} }
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_prekeys_available_prekeys_request_failed", error);
}
DDLogError(@"%@ Failed to retrieve the number of available prekeys.", self.tag); DDLogError(@"%@ Failed to retrieve the number of available prekeys.", self.tag);
// Mark the prekeys as _NOT_ checked on failure. // Mark the prekeys as _NOT_ checked on failure.

@ -166,45 +166,49 @@ NS_ASSUME_NONNULL_BEGIN
TSRequest *request = [[TSContactsIntersectionRequest alloc] initWithHashesArray:hashes]; TSRequest *request = [[TSContactsIntersectionRequest alloc] initWithHashesArray:hashes];
[[TSNetworkManager sharedManager] makeRequest:request [[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *tsTask, id responseDict) { success:^(NSURLSessionDataTask *tsTask, id responseDict) {
NSMutableDictionary *attributesForIdentifier = [NSMutableDictionary dictionary]; NSMutableDictionary *attributesForIdentifier = [NSMutableDictionary dictionary];
NSArray *contactsArray = [(NSDictionary *)responseDict objectForKey:@"contacts"]; NSArray *contactsArray = [(NSDictionary *)responseDict objectForKey:@"contacts"];
// Map attributes to phone numbers // Map attributes to phone numbers
if (contactsArray) { if (contactsArray) {
for (NSDictionary *dict in contactsArray) { for (NSDictionary *dict in contactsArray) {
NSString *hash = [dict objectForKey:@"token"]; NSString *hash = [dict objectForKey:@"token"];
NSString *identifier = [phoneNumbersByHashes objectForKey:hash]; NSString *identifier = [phoneNumbersByHashes objectForKey:hash];
if (!identifier) { if (!identifier) {
DDLogWarn(@"%@ An interesecting hash wasn't found in the mapping.", self.tag); DDLogWarn(@"%@ An interesecting hash wasn't found in the mapping.", self.tag);
break; break;
} }
[attributesForIdentifier setObject:dict forKey:identifier]; [attributesForIdentifier setObject:dict forKey:identifier];
} }
} }
// Insert or update contact attributes // Insert or update contact attributes
[[TSStorageManager sharedManager].dbReadWriteConnection [[TSStorageManager sharedManager].dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in attributesForIdentifier) { for (NSString *identifier in attributesForIdentifier) {
SignalRecipient *recipient = SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:identifier
[SignalRecipient recipientWithTextSecureIdentifier:identifier withTransaction:transaction]; withTransaction:transaction];
if (!recipient) { if (!recipient) {
recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier relay:nil]; recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier relay:nil];
} }
NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier]; NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier];
recipient.relay = attributes[@"relay"]; recipient.relay = attributes[@"relay"];
[recipient saveWithTransaction:transaction]; [recipient saveWithTransaction:transaction];
} }
}]; }];
success([NSSet setWithArray:attributesForIdentifier.allKeys]); success([NSSet setWithArray:attributesForIdentifier.allKeys]);
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"contacts_error_contacts_intersection_failed", error);
}
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
if (response.statusCode == 413) { if (response.statusCode == 413) {
failure(OWSErrorWithCodeDescription( failure(OWSErrorWithCodeDescription(

@ -143,67 +143,68 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
TSAttachmentRequest *attachmentRequest = [[TSAttachmentRequest alloc] initWithId:attachment.serverId relay:attachment.relay]; TSAttachmentRequest *attachmentRequest = [[TSAttachmentRequest alloc] initWithId:attachment.serverId relay:attachment.relay];
[self.networkManager makeRequest:attachmentRequest [self.networkManager makeRequest:attachmentRequest
success:^(NSURLSessionDataTask *task, id responseObject) { success:^(NSURLSessionDataTask *task, id responseObject) {
if (![responseObject isKindOfClass:[NSDictionary class]]) { if (![responseObject isKindOfClass:[NSDictionary class]]) {
DDLogError(@"%@ Failed retrieval of attachment. Response had unexpected format.", DDLogError(@"%@ Failed retrieval of attachment. Response had unexpected format.", self.tag);
self.tag); NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
NSError *error = OWSErrorMakeUnableToProcessServerResponseError(); return markAndHandleFailure(error);
return markAndHandleFailure(error); }
} NSString *location = [(NSDictionary *)responseObject objectForKey:@"location"];
NSString *location = [(NSDictionary *)responseObject objectForKey:@"location"]; if (!location) {
if (!location) { DDLogError(@"%@ Failed retrieval of attachment. Response had no location.", self.tag);
DDLogError( NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
@"%@ Failed retrieval of attachment. Response had no location.", self.tag); return markAndHandleFailure(error);
NSError *error = OWSErrorMakeUnableToProcessServerResponseError(); }
return markAndHandleFailure(error);
} dispatch_async([OWSDispatch attachmentsQueue], ^{
[self downloadFromLocation:location
dispatch_async([OWSDispatch attachmentsQueue], ^{ pointer:attachment
[self downloadFromLocation:location success:^(NSData *_Nonnull encryptedData) {
pointer:attachment [self decryptAttachmentData:encryptedData
success:^(NSData *_Nonnull encryptedData) { pointer:attachment
[self decryptAttachmentData:encryptedData success:markAndHandleSuccess
pointer:attachment failure:markAndHandleFailure];
success:markAndHandleSuccess }
failure:markAndHandleFailure]; failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
} if (attachment.serverId < 100) {
failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) { // This looks like the symptom of the "frequent 404
if (attachment.serverId < 100) { // downloading attachments with low server ids".
// This looks like the symptom of the "frequent 404 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
// downloading attachments with low server ids". NSInteger statusCode = [httpResponse statusCode];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response; DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@",
NSInteger statusCode = [httpResponse statusCode]; self.tag,
DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@", (int)statusCode,
self.tag, (unsigned long long)attachment.serverId,
(int)statusCode, error);
(unsigned long long)attachment.serverId, [DDLog flushLog];
error); OWSAssert(0);
[DDLog flushLog]; }
OWSAssert(0); if (markAndHandleFailure) {
} markAndHandleFailure(error);
if (markAndHandleFailure) { }
markAndHandleFailure(error); }];
} });
}]; }
}); failure:^(NSURLSessionDataTask *task, NSError *error) {
} if (!IsNSErrorNetworkFailure(error)) {
failure:^(NSURLSessionDataTask *task, NSError *error) { OWSProdErrorWNSError(@"error_attachment_request_failed", error);
DDLogError(@"Failed retrieval of attachment with error: %@", error); }
if (attachment.serverId < 100) { DDLogError(@"Failed retrieval of attachment with error: %@", error);
// This _shouldn't_ be the symptom of the "frequent 404 if (attachment.serverId < 100) {
// downloading attachments with low server ids". // This _shouldn't_ be the symptom of the "frequent 404
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response; // downloading attachments with low server ids".
NSInteger statusCode = [httpResponse statusCode]; NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@", NSInteger statusCode = [httpResponse statusCode];
self.tag, DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@",
(int)statusCode, self.tag,
(unsigned long long)attachment.serverId, (int)statusCode,
error); (unsigned long long)attachment.serverId,
[DDLog flushLog]; error);
OWSAssert(0); [DDLog flushLog];
} OWSAssert(0);
return markAndHandleFailure(error); }
}]; return markAndHandleFailure(error);
}];
} }
- (void)decryptAttachmentData:(NSData *)cipherText - (void)decryptAttachmentData:(NSData *)cipherText

@ -186,7 +186,7 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
_successHandler = ^{ _successHandler = ^{
typeof(self) strongSelf = weakSelf; typeof(self) strongSelf = weakSelf;
if (!strongSelf) { if (!strongSelf) {
OWSCAssert(NO); OWSProdCFail(@"message_sender_error_send_operation_did_not_complete");
return; return;
} }
@ -200,7 +200,7 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
_failureHandler = ^(NSError *_Nonnull error) { _failureHandler = ^(NSError *_Nonnull error) {
typeof(self) strongSelf = weakSelf; typeof(self) strongSelf = weakSelf;
if (!strongSelf) { if (!strongSelf) {
OWSCAssert(NO); OWSProdCFail(@"message_sender_error_send_operation_did_not_complete");
return; return;
} }
@ -474,7 +474,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[TSAttachmentStream fetchObjectWithUniqueID:message.attachmentIds.firstObject]; [TSAttachmentStream fetchObjectWithUniqueID:message.attachmentIds.firstObject];
if (!attachmentStream) { if (!attachmentStream) {
DDLogError(@"%@ Unable to find local saved attachment to upload.", self.tag); OWSProdError(@"message_sender_error_could_not_load_attachment");
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
// Not finding local attachment is a terminal failure. // Not finding local attachment is a terminal failure.
[error setIsRetryable:NO]; [error setIsRetryable:NO];
@ -539,7 +539,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSError *error; NSError *error;
[attachmentStream writeData:dataCopy error:&error]; [attachmentStream writeData:dataCopy error:&error];
if (error) { if (error) {
DDLogError(@"%@ Failed to write data for outgoing attachment with error:%@", self.tag, error); OWSProdErrorWNSError(@"message_sender_error_could_not_write_attachment", error);
return failureHandler(error); return failureHandler(error);
} }
@ -575,7 +575,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (recipients.count == 0 && !*error) { if (recipients.count == 0 && !*error) {
// error should be set in contactsUpater, but just in case. // error should be set in contactsUpater, but just in case.
DDLogError(@"%@ Unknown error finding contacts", self.tag); OWSProdError(@"message_sender_error_could_not_find_contacts_1");
*error = OWSErrorMakeFailedToSendOutgoingMessageError(); *error = OWSErrorMakeFailedToSendOutgoingMessageError();
} }
@ -598,7 +598,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (recipients.count == 0) { if (recipients.count == 0) {
if (!error) { if (!error) {
DDLogError(@"%@ Unknown error finding contacts", self.tag); OWSProdError(@"message_sender_error_could_not_find_contacts_2");
error = OWSErrorMakeFailedToSendOutgoingMessageError(); error = OWSErrorMakeFailedToSendOutgoingMessageError();
} }
// If no recipients were found, there's no reason to retry. It will just fail again. // If no recipients were found, there's no reason to retry. It will just fail again.
@ -652,7 +652,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self unregisteredRecipient:recipient message:message thread:thread]; [self unregisteredRecipient:recipient message:message thread:thread];
} }
DDLogError(@"%@ contact lookup failed with error: %@", self.tag, error); OWSProdErrorWNSError(@"message_sender_error_could_not_find_contacts_3", error);
// No need to repeat trying to find a failure. Apart from repeatedly failing, it would also cause us // No need to repeat trying to find a failure. Apart from repeatedly failing, it would also cause us
// to print redundant error messages. // to print redundant error messages.
[error setIsRetryable:NO]; [error setIsRetryable:NO];
@ -678,10 +678,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
success:successHandler success:successHandler
failure:failureHandler]; failure:failureHandler];
} else { } else {
DDLogError(@"%@ Unexpected unhandlable message: %@", self.tag, message);
// Neither a group nor contact thread? This should never happen. // Neither a group nor contact thread? This should never happen.
OWSAssert(NO); OWSFail(@"%@ Unknown message type: %@", self.tag, NSStringFromClass([message class]));
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:NO]; [error setIsRetryable:NO];
@ -851,7 +849,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
DDLogWarn(@"%@ Failed to update prekeys with the server: %@", self.tag, error); DDLogWarn(@"%@ Failed to update prekeys with the server: %@", self.tag, error);
}]; }];
DDLogError(@"%@ Message send failed due to repeated inability to update prekeys.", self.tag);
NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError(); NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError();
[error setIsRetryable:YES]; [error setIsRetryable:YES];
return failureHandler(error); return failureHandler(error);
@ -859,8 +856,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (remainingAttempts <= 0) { if (remainingAttempts <= 0) {
// We should always fail with a specific error. // We should always fail with a specific error.
DDLogError(@"%@ Unexpected generic failure.", self.tag); OWSProdFail(@"message_sender_error_generic_send_failure");
OWSAssert(NO);
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:YES]; [error setIsRetryable:YES];
@ -897,7 +893,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
PreKeyBundle *newKeyBundle = exception.userInfo[TSInvalidPreKeyBundleKey]; PreKeyBundle *newKeyBundle = exception.userInfo[TSInvalidPreKeyBundleKey];
if (![newKeyBundle isKindOfClass:[PreKeyBundle class]]) { if (![newKeyBundle isKindOfClass:[PreKeyBundle class]]) {
OWSFail(@"%@ unexpected TSInvalidPreKeyBundleKey: %@", self.tag, newKeyBundle); OWSProdFail(@"message_sender_error_unexpected_key_bundle");
failureHandler(error); failureHandler(error);
return; return;
} }
@ -905,14 +901,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSData *newIdentityKeyWithVersion = newKeyBundle.identityKey; NSData *newIdentityKeyWithVersion = newKeyBundle.identityKey;
if (![newIdentityKeyWithVersion isKindOfClass:[NSData class]]) { if (![newIdentityKeyWithVersion isKindOfClass:[NSData class]]) {
OWSFail(@"%@ unexpected TSInvalidRecipientKey: %@", self.tag, newIdentityKeyWithVersion); OWSProdFail(@"message_sender_error_invalid_identity_key_type");
failureHandler(error); failureHandler(error);
return; return;
} }
// TODO migrate to storing the full 33 byte representation of the identity key. // TODO migrate to storing the full 33 byte representation of the identity key.
if (newIdentityKeyWithVersion.length != kIdentityKeyLength) { if (newIdentityKeyWithVersion.length != kIdentityKeyLength) {
OWSFail(@"%@ unexpected key length: %lu", self.tag, (unsigned long)newIdentityKeyWithVersion.length); OWSProdFail(@"message_sender_error_invalid_identity_key_length");
failureHandler(error); failureHandler(error);
return; return;
} }
@ -1017,7 +1013,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSDictionary *serializedResponse = NSDictionary *serializedResponse =
[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
if (error) { if (error) {
DDLogError(@"%@ Failed to serialize response of mismatched devices: %@", self.tag, error); OWSProdErrorWNSError(@"message_sender_error_could_not_parse_mismatched_devices_json", error);
[error setIsRetryable:YES]; [error setIsRetryable:YES];
return failureHandler(error); return failureHandler(error);
} }
@ -1057,8 +1053,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch sessionStoreQueue], ^{ dispatch_async([OWSDispatch sessionStoreQueue], ^{
if (extraDevices.count < 1 && missingDevices.count < 1) { if (extraDevices.count < 1 && missingDevices.count < 1) {
DDLogError(@"%@ No missing or extra devices in %s", self.tag, __PRETTY_FUNCTION__); OWSProdFail(@"message_sender_error_no_missing_or_extra_devices");
OWSAssert(NO);
} }
if (extraDevices && extraDevices.count > 0) { if (extraDevices && extraDevices.count > 0) {
@ -1221,7 +1216,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_semaphore_signal(sema); dispatch_semaphore_signal(sema);
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"Server replied on PreKeyBundle request with error: %@", error); if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"message_sender_error_recipient_prekey_request_failed", error);
}
DDLogError(@"Server replied to PreKeyBundle request with error: %@", error);
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
if (response.statusCode == 404) { if (response.statusCode == 404) {
// Can't throw exception from within callback as it's probabably a different thread. // Can't throw exception from within callback as it's probabably a different thread.
@ -1292,7 +1290,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error]; NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];
if (error) { if (error) {
DDLogError(@"Error while making JSON dictionary of message: %@", error.debugDescription); OWSProdErrorWNSError(@"message_send_error_could_not_serialize_message_json", error);
return nil; return nil;
} }

@ -42,6 +42,32 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
#define kOWSProdAssertParameterEnvelopeIsLegacy @"envelope_is_legacy"
#define kOWSProdAssertParameterEnvelopeHasContent @"has_content"
#define kOWSProdAssertParameterEnvelopeDescription @"envelope_description"
#define kOWSProdAssertParameterEnvelopeEncryptedLength @"encrypted_length"
#define AnalyticsParametersFromEnvelope(__envelope) \
^{ \
NSData *__encryptedData = __envelope.hasContent ? __envelope.content : __envelope.legacyMessage; \
return (@{ \
kOWSProdAssertParameterEnvelopeIsLegacy : @(__envelope.hasLegacyMessage), \
kOWSProdAssertParameterEnvelopeHasContent : @(__envelope.hasContent), \
kOWSProdAssertParameterEnvelopeDescription : [self descriptionForEnvelopeType:__envelope], \
kOWSProdAssertParameterEnvelopeEncryptedLength : @(__encryptedData.length), \
}); \
}
#define OWSProdErrorWEnvelope(__analyticsEventName, __envelope) \
{ \
DDLogError(@"%s:%d %@: %@", \
__PRETTY_FUNCTION__, \
__LINE__, \
__analyticsEventName, \
[self descriptionForEnvelope:__envelope]); \
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromEnvelope(__envelope)) \
}
@interface TSMessagesManager () @interface TSMessagesManager ()
@property (nonatomic, readonly) id<OWSCallMessageHandler> callMessageHandler; @property (nonatomic, readonly) id<OWSCallMessageHandler> callMessageHandler;
@ -135,39 +161,38 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Debugging #pragma mark - Debugging
- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - (NSString *)descriptionForEnvelopeType:(OWSSignalServiceProtosEnvelope *)envelope
{ {
OWSAssert(envelope != nil); OWSAssert(envelope != nil);
NSString *envelopeType;
switch (envelope.type) { switch (envelope.type) {
case OWSSignalServiceProtosEnvelopeTypeReceipt: case OWSSignalServiceProtosEnvelopeTypeReceipt:
envelopeType = @"DeliveryReceipt"; return @"DeliveryReceipt";
break;
case OWSSignalServiceProtosEnvelopeTypeUnknown: case OWSSignalServiceProtosEnvelopeTypeUnknown:
// Shouldn't happen // Shouldn't happen
OWSAssert(NO); OWSProdFail(@"message_manager_error_envelope_type_unknown");
envelopeType = @"Unknown"; return @"Unknown";
break;
case OWSSignalServiceProtosEnvelopeTypeCiphertext: case OWSSignalServiceProtosEnvelopeTypeCiphertext:
envelopeType = @"SignalEncryptedMessage"; return @"SignalEncryptedMessage";
break;
case OWSSignalServiceProtosEnvelopeTypeKeyExchange: case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
// Unsupported // Unsupported
OWSAssert(NO); OWSProdFail(@"message_manager_error_envelope_type_key_exchange");
envelopeType = @"KeyExchange"; return @"KeyExchange";
break;
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: case OWSSignalServiceProtosEnvelopeTypePrekeyBundle:
envelopeType = @"PreKeyEncryptedMessage"; return @"PreKeyEncryptedMessage";
break;
default: default:
// Shouldn't happen // Shouldn't happen
OWSAssert(NO); OWSProdFail(@"message_manager_error_envelope_type_other");
envelopeType = @"Other"; return @"Other";
break;
} }
}
- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
{
OWSAssert(envelope != nil);
return [NSString stringWithFormat:@"<Envelope type: %@, source: %@.%d, timestamp: %llu content.length: %lu />", return [NSString stringWithFormat:@"<Envelope type: %@, source: %@.%d, timestamp: %llu content.length: %lu />",
envelopeType, [self descriptionForEnvelopeType:envelope],
envelope.source, envelope.source,
(unsigned int)envelope.sourceDevice, (unsigned int)envelope.sourceDevice,
envelope.timestamp, envelope.timestamp,
@ -189,7 +214,9 @@ NS_ASSUME_NONNULL_BEGIN
} else if (content.hasNullMessage) { } else if (content.hasNullMessage) {
return [NSString stringWithFormat:@"<NullMessage: %@ />", content.nullMessage]; return [NSString stringWithFormat:@"<NullMessage: %@ />", content.nullMessage];
} else { } else {
OWSAssert(NO); // Don't fire an analytics event; if we ever add a new content type, we'd generate a ton of
// analytics traffic.
OWSFail(@"Unknown content type.");
return @"UnknownContent"; return @"UnknownContent";
} }
} }
@ -233,9 +260,11 @@ NS_ASSUME_NONNULL_BEGIN
[description appendString:@"ContactRequest"]; [description appendString:@"ContactRequest"];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) { } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
[description appendString:@"GroupRequest"]; [description appendString:@"GroupRequest"];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeBlocked) {
[description appendString:@"BlockedRequest"];
} else { } else {
// Shouldn't happen // Shouldn't happen
OWSAssert(NO); OWSFail(@"Unknown sync message request type");
[description appendString:@"UnknownRequest"]; [description appendString:@"UnknownRequest"];
} }
} else if (syncMessage.hasBlocked) { } else if (syncMessage.hasBlocked) {
@ -248,7 +277,7 @@ NS_ASSUME_NONNULL_BEGIN
[description appendString:verifiedString]; [description appendString:verifiedString];
} else { } else {
// Shouldn't happen // Shouldn't happen
OWSAssert(NO); OWSFail(@"Unknown sync message type");
[description appendString:@"Unknown"]; [description appendString:@"Unknown"];
} }
@ -295,6 +324,7 @@ NS_ASSUME_NONNULL_BEGIN
envelope.source, envelope.source,
(unsigned int)envelope.sourceDevice, (unsigned int)envelope.sourceDevice,
error); error);
OWSProdError(@"message_manager_error_could_not_handle_secure_message");
} }
completion(); completion();
}]; }];
@ -312,6 +342,7 @@ NS_ASSUME_NONNULL_BEGIN
envelope.source, envelope.source,
(unsigned int)envelope.sourceDevice, (unsigned int)envelope.sourceDevice,
error); error);
OWSProdError(@"message_manager_error_could_not_handle_prekey_bundle");
} }
completion(); completion();
}]; }];
@ -336,6 +367,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
} @catch (NSException *exception) { } @catch (NSException *exception) {
DDLogError(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription); DDLogError(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription);
OWSProdFailWNSException(@"message_manager_error_invalid_protocol_message", exception);
} }
completion(); completion();
@ -367,15 +399,18 @@ NS_ASSUME_NONNULL_BEGIN
NSData *encryptedData NSData *encryptedData
= messageEnvelope.hasContent ? messageEnvelope.content : messageEnvelope.legacyMessage; = messageEnvelope.hasContent ? messageEnvelope.content : messageEnvelope.legacyMessage;
if (!encryptedData) { if (!encryptedData) {
DDLogError(@"Skipping message envelope which had no encrypted data."); OWSProdFail(@"message_manager_error_message_envelope_has_no_content");
completion(nil); completion(nil);
return; return;
} }
NSUInteger kMaxEncryptedDataLength = 250 * 1024; NSUInteger kMaxEncryptedDataLength = 250 * 1024;
if (encryptedData.length > kMaxEncryptedDataLength) { if (encryptedData.length > kMaxEncryptedDataLength) {
DDLogError(@"Skipping message envelope with oversize encrypted data: %lu.", OWSProdErrorWParams(@"message_manager_error_oversize_message", ^{
(unsigned long)encryptedData.length); return (@{
@"message_size" : @([OWSAnalytics orderOfMagnitudeOf:(long)encryptedData.length]),
});
});
completion(nil); completion(nil);
return; return;
} }
@ -424,7 +459,7 @@ NS_ASSUME_NONNULL_BEGIN
// DEPRECATED - Remove after all clients have been upgraded. // DEPRECATED - Remove after all clients have been upgraded.
NSData *encryptedData = preKeyEnvelope.hasContent ? preKeyEnvelope.content : preKeyEnvelope.legacyMessage; NSData *encryptedData = preKeyEnvelope.hasContent ? preKeyEnvelope.content : preKeyEnvelope.legacyMessage;
if (!encryptedData) { if (!encryptedData) {
DDLogError(@"Skipping message envelope which had no encrypted data"); OWSProdFail(@"message_manager_error_prekey_bundle_envelope_has_no_content");
completion(nil); completion(nil);
return; return;
} }
@ -816,7 +851,7 @@ NS_ASSUME_NONNULL_BEGIN
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil; NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
if (!groupId) { if (!groupId) {
OWSAssert(groupId); OWSFail(@"Group info request is missing group id.");
return; return;
} }
@ -1009,24 +1044,30 @@ NS_ASSUME_NONNULL_BEGIN
__block TSErrorMessage *errorMessage; __block TSErrorMessage *errorMessage;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if ([exception.name isEqualToString:NoSessionException]) { if ([exception.name isEqualToString:NoSessionException]) {
OWSProdErrorWEnvelope(@"message_manager_error_no_session", envelope);
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction]; errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyException]) { } else if ([exception.name isEqualToString:InvalidKeyException]) {
OWSProdErrorWEnvelope(@"message_manager_error_invalid_key", envelope);
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction]; errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyIdException]) { } else if ([exception.name isEqualToString:InvalidKeyIdException]) {
OWSProdErrorWEnvelope(@"message_manager_error_invalid_key_id", envelope);
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction]; errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:DuplicateMessageException]) { } else if ([exception.name isEqualToString:DuplicateMessageException]) {
// Duplicate messages are dismissed // Duplicate messages are dismissed
return; return;
} else if ([exception.name isEqualToString:InvalidVersionException]) { } else if ([exception.name isEqualToString:InvalidVersionException]) {
OWSProdErrorWEnvelope(@"message_manager_error_invalid_message_version", envelope);
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction]; errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) { } else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
// Should no longer get here, since we now record the new identity for incoming messages. // Should no longer get here, since we now record the new identity for incoming messages.
OWSProdErrorWEnvelope(@"message_manager_error_untrusted_identity_key_exception", envelope);
OWSFail(@"%@ Failed to trust identity on incoming message from: %@.%d", OWSFail(@"%@ Failed to trust identity on incoming message from: %@.%d",
self.tag, self.tag,
envelope.source, envelope.source,
envelope.sourceDevice); envelope.sourceDevice);
return; return;
} else { } else {
OWSProdErrorWEnvelope(@"message_manager_error_corrupt_message", envelope);
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction]; errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
} }

@ -1,4 +1,6 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved. //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDeviceProvisioningCodeService.h" #import "OWSDeviceProvisioningCodeService.h"
#import "OWSDeviceProvisioningCodeRequest.h" #import "OWSDeviceProvisioningCodeRequest.h"
@ -48,6 +50,9 @@ NSString *const OWSDeviceProvisioningCodeServiceProvisioningCodeKey = @"verifica
} }
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_provisioning_code_request_failed", error);
}
DDLogVerbose(@"ProvisioningCode request failed with error: %@", error); DDLogVerbose(@"ProvisioningCode request failed with error: %@", error);
failureCallback(error); failureCallback(error);
}]; }];

@ -1,4 +1,6 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved. //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDeviceProvisioningService.h" #import "OWSDeviceProvisioningService.h"
#import "OWSDeviceProvisioningRequest.h" #import "OWSDeviceProvisioningRequest.h"
@ -45,6 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
successCallback(); successCallback();
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_provisioning_request_failed", error);
}
DDLogVerbose(@"Provisioning request failed with error: %@", error); DDLogVerbose(@"Provisioning request failed with error: %@", error);
failureCallback(error); failureCallback(error);
}]; }];

@ -1,4 +1,6 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved. //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDevicesService.h" #import "OWSDevicesService.h"
#import "OWSDeleteDeviceRequest.h" #import "OWSDeleteDeviceRequest.h"
@ -30,6 +32,9 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_get_devices_failed", error);
}
DDLogVerbose(@"Get devices request failed with error: %@", error); DDLogVerbose(@"Get devices request failed with error: %@", error);
failureCallback(error); failureCallback(error);
}]; }];
@ -47,6 +52,9 @@ NS_ASSUME_NONNULL_BEGIN
successCallback(); successCallback();
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {
OWSProdErrorWNSError(@"error_unlink_device_failed", error);
}
DDLogVerbose(@"Get devices request failed with error: %@", error); DDLogVerbose(@"Get devices request failed with error: %@", error);
failureCallback(error); failureCallback(error);
}]; }];

@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN
extern NSString *const TSNetworkManagerDomain; extern NSString *const TSNetworkManagerDomain;
BOOL IsNSErrorNetworkFailure(NSError *_Nullable error);
@interface TSNetworkManager : NSObject @interface TSNetworkManager : NSObject
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;

@ -12,6 +12,11 @@
NSString *const TSNetworkManagerDomain = @"org.whispersystems.signal.networkManager"; NSString *const TSNetworkManagerDomain = @"org.whispersystems.signal.networkManager";
BOOL IsNSErrorNetworkFailure(NSError *_Nullable error)
{
return ([error.domain isEqualToString:TSNetworkManagerDomain] && error.code == 0);
}
@interface TSNetworkManager () @interface TSNetworkManager ()
typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error); typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);

@ -85,7 +85,11 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
- (nullable Class)unarchiver:(NSKeyedUnarchiver *)unarchiver cannotDecodeObjectOfClassName:(NSString *)name originalClasses:(NSArray<NSString *> *)classNames - (nullable Class)unarchiver:(NSKeyedUnarchiver *)unarchiver cannotDecodeObjectOfClassName:(NSString *)name originalClasses:(NSArray<NSString *> *)classNames
{ {
DDLogError(@"[OWSUnarchiverDelegate] Ignoring unknown class name: %@. Was the class definition deleted?", name); OWSProdErrorWParams(@"storage_error_could_not_decode_class", ^{
return (@{
@"class_name" : name,
});
});
return [OWSUnknownObject class]; return [OWSUnknownObject class];
} }
@ -180,13 +184,14 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
return [unarchiver decodeObjectForKey:@"root"]; return [unarchiver decodeObjectForKey:@"root"];
} @catch (NSException *exception) { } @catch (NSException *exception) {
// Sync log in case we bail. // Sync log in case we bail.
DDLogError(@"%@ Unarchiving key:%@ from collection:%@ and data %@ failed with error: %@", OWSProdErrorWParams(@"storage_error_deserialization", ^{
self.tag, return (@{
key, @"collection" : collection,
collection, kOWSProdAssertParameterNSExceptionName : exception.name,
data, kOWSProdAssertParameterNSExceptionReason : exception.reason,
exception.reason); kOWSProdAssertParameterNSExceptionClassName : NSStringFromClass([exception class]),
DDLogError(@"%@ Raising exception.", self.tag); });
});
@throw exception; @throw exception;
} }
}; };
@ -255,7 +260,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
BOOL success = [ressourceURL setResourceValues:resourcesAttrs error:&error]; BOOL success = [ressourceURL setResourceValues:resourcesAttrs error:&error];
if (error || !success) { if (error || !success) {
DDLogError(@"Error while removing files from backup: %@", error.description); OWSProdErrorWNSError(@"storage_error_file_protecion", error);
return; return;
} }
} }
@ -353,7 +358,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
BOOL shouldHavePassword = [NSFileManager.defaultManager fileExistsAtPath:[self dbPath]]; BOOL shouldHavePassword = [NSFileManager.defaultManager fileExistsAtPath:[self dbPath]];
if (shouldHavePassword) { if (shouldHavePassword) {
OWSProdErrorWNSError(@"storage_error_could_not_load_database_second_attempt", keyFetchError); OWSProdError(@"storage_error_could_not_load_database_second_attempt");
} }
// Try to reset app by deleting database. // Try to reset app by deleting database.
@ -372,14 +377,14 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
NSError *keySetError; NSError *keySetError;
[SAMKeychain setPassword:newDBPassword forService:keychainService account:keychainDBPassAccount error:&keySetError]; [SAMKeychain setPassword:newDBPassword forService:keychainService account:keychainDBPassAccount error:&keySetError];
if (keySetError) { if (keySetError) {
DDLogError(@"%@ Setting DB password failed with error: %@", self.tag, keySetError); OWSProdErrorWNSError(@"storage_error_could_not_store_database_password", keySetError);
[self deletePasswordFromKeychain]; [self deletePasswordFromKeychain];
[NSException raise:TSStorageManagerExceptionNameDatabasePasswordUnwritable [NSException raise:TSStorageManagerExceptionNameDatabasePasswordUnwritable
format:@"Setting DB password failed with error: %@", keySetError]; format:@"Setting DB password failed with error: %@", keySetError];
} else { } else {
DDLogError(@"Succesfully set new DB password."); DDLogWarn(@"Succesfully set new DB password.");
} }
return newDBPassword; return newDBPassword;

@ -48,6 +48,8 @@ typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) {
+ (void)appLaunchDidComplete; + (void)appLaunchDidComplete;
+ (long)orderOfMagnitudeOf:(long)value;
@end @end
typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)(); typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)();
@ -56,6 +58,9 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
#define kOWSProdAssertParameterNSErrorDomain @"nserror_domain" #define kOWSProdAssertParameterNSErrorDomain @"nserror_domain"
#define kOWSProdAssertParameterNSErrorCode @"nserror_code" #define kOWSProdAssertParameterNSErrorCode @"nserror_code"
#define kOWSProdAssertParameterNSErrorDescription @"nserror_description" #define kOWSProdAssertParameterNSErrorDescription @"nserror_description"
#define kOWSProdAssertParameterNSExceptionName @"nsexception_name"
#define kOWSProdAssertParameterNSExceptionReason @"nsexception_reason"
#define kOWSProdAssertParameterNSExceptionClassName @"nsexception_classname"
// These methods should be used to assert errors for which we want to fire analytics events. // These methods should be used to assert errors for which we want to fire analytics events.
// //
@ -64,8 +69,7 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
// //
// parametersBlock is of type OWSProdAssertParametersBlock. // parametersBlock is of type OWSProdAssertParametersBlock.
// The "C" variants (e.g. OWSProdAssert() vs. OWSProdCAssert() should be used in free functions, // The "C" variants (e.g. OWSProdAssert() vs. OWSProdCAssert() should be used in free functions,
// where there is no self. // where there is no self. They can also be used in blocks to avoid capturing a reference to self.
//
#define OWSProdAssertWParamsTemplate(__value, __analyticsEventName, __parametersBlock, __assertMacro) \ #define OWSProdAssertWParamsTemplate(__value, __analyticsEventName, __parametersBlock, __assertMacro) \
{ \ { \
if (!(BOOL)(__value)) { \ if (!(BOOL)(__value)) { \
@ -115,14 +119,33 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
#define AnalyticsParametersFromNSError(__nserror) \ #define AnalyticsParametersFromNSError(__nserror) \
^{ \ ^{ \
return (@{ \ return (@{ \
kOWSProdAssertParameterNSErrorDomain : __nserror.domain, \ kOWSProdAssertParameterNSErrorDomain : (__nserror.domain ?: @"unknown"), \
kOWSProdAssertParameterNSErrorCode : @(__nserror.code), \ kOWSProdAssertParameterNSErrorCode : @(__nserror.code), \
kOWSProdAssertParameterNSErrorDescription : __nserror.description, \ kOWSProdAssertParameterNSErrorDescription : (__nserror.description ?: @"unknown"), \
}); \
}
#define AnalyticsParametersFromNSException(__exception) \
^{ \
return (@{ \
kOWSProdAssertParameterNSExceptionName : (__exception.name ?: @"unknown"), \
kOWSProdAssertParameterNSExceptionReason : (__exception.reason ?: @"unknown"), \
kOWSProdAssertParameterNSExceptionClassName : \
(__exception ? NSStringFromClass([__exception class]) : @"unknown"), \
}); \ }); \
} }
#define OWSProdFailWNSError(__analyticsEventName, __nserror) \ #define OWSProdFailWNSError(__analyticsEventName, __nserror) \
OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) { \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) \
}
#define OWSProdFailWNSException(__analyticsEventName, __exception) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __exception); \
OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
}
#define OWSProdEventWParams(__severityLevel, __analyticsEventName, __parametersBlock) \ #define OWSProdEventWParams(__severityLevel, __analyticsEventName, __parametersBlock) \
{ \ { \
@ -148,6 +171,15 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
#define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil) #define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil)
#define OWSProdErrorWNSError(__analyticsEventName, __nserror) \ #define OWSProdErrorWNSError(__analyticsEventName, __nserror) \
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) { \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) \
}
#define OWSProdErrorWNSException(__analyticsEventName, __exception) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __exception); \
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
}
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -12,11 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@interface OWSAnalytics (Test) #pragma mark -
+ (long)orderOfMagnitudeOf:(long)value;
@end
@implementation OWSAnalyticsTests @implementation OWSAnalyticsTests

Loading…
Cancel
Save