session-ios/SignalServiceKit/src/Messages/TSMessagesManager.m

1345 lines
61 KiB
Mathematica
Raw Normal View History

//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
2015-12-07 03:31:43 +01:00
#import "TSMessagesManager.h"
#import "ContactsManagerProtocol.h"
#import "ContactsUpdater.h"
#import "Cryptography.h"
#import "DataSource.h"
#import "MimeTypeUtil.h"
2015-12-07 03:31:43 +01:00
#import "NSData+messagePadding.h"
#import "NSDate+millisecondTimeStamp.h"
#import "NotificationsProtocol.h"
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
#import "OWSAttachmentsProcessor.h"
2017-04-03 20:42:04 +02:00
#import "OWSBlockingManager.h"
#import "OWSCallMessageHandler.h"
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSError.h"
2017-02-16 00:32:27 +01:00
#import "OWSIncomingMessageFinder.h"
#import "OWSIncomingSentMessageTranscript.h"
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
#import "OWSMessageSender.h"
2016-09-01 16:28:35 +02:00
#import "OWSReadReceiptsProcessor.h"
#import "OWSRecordTranscriptJob.h"
#import "OWSSyncContactsMessage.h"
#import "OWSSyncGroupsMessage.h"
#import "OWSSyncGroupsRequestMessage.h"
#import "ProfileManagerProtocol.h"
#import "TSAccountManager.h"
2015-12-07 03:31:43 +01:00
#import "TSAttachmentStream.h"
#import "TSCall.h"
#import "TSContactThread.h"
2015-12-07 03:31:43 +01:00
#import "TSDatabaseView.h"
#import "TSGroupModel.h"
#import "TSGroupThread.h"
2015-12-07 03:31:43 +01:00
#import "TSInfoMessage.h"
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "TSNetworkManager.h"
2017-02-10 01:35:10 +01:00
#import "TSPreKeyManager.h"
2015-12-07 03:31:43 +01:00
#import "TSStorageHeaders.h"
#import "TextSecureKitEnv.h"
#import <AxolotlKit/AxolotlExceptions.h>
#import <AxolotlKit/SessionCipher.h>
2015-12-07 03:31:43 +01:00
NS_ASSUME_NONNULL_BEGIN
// We need to use a consistent batch size throughout
// the incoming message pipeline (i.e. in the
// "decrypt" and "process" steps), or the pipeline
// doesn't flow smoothly.
//
// We want a value that is just high enough to yield
// perf benefits. The right value is probably 5-15.
const NSUInteger kIncomingMessageBatchSize = 10;
@interface TSMessagesManager ()
@property (nonatomic, readonly) id<OWSCallMessageHandler> callMessageHandler;
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) TSStorageManager *storageManager;
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
@property (nonatomic, readonly) OWSMessageSender *messageSender;
2017-02-16 00:32:27 +01:00
@property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder;
2017-04-03 20:42:04 +02:00
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
@property (nonatomic, readonly) OWSIdentityManager *identityManager;
@end
#pragma mark -
2015-12-07 03:31:43 +01:00
@implementation TSMessagesManager
+ (instancetype)sharedManager {
static TSMessagesManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
sharedMyManager = [[self alloc] initDefault];
2015-12-07 03:31:43 +01:00
});
return sharedMyManager;
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
- (instancetype)initDefault
{
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
TSNetworkManager *networkManager = [TSNetworkManager sharedManager];
TSStorageManager *storageManager = [TSStorageManager sharedManager];
id<ContactsManagerProtocol> contactsManager = [TextSecureKitEnv sharedEnv].contactsManager;
id<OWSCallMessageHandler> callMessageHandler = [TextSecureKitEnv sharedEnv].callMessageHandler;
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager];
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
return [self initWithNetworkManager:networkManager
storageManager:storageManager
callMessageHandler:callMessageHandler
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
contactsManager:contactsManager
contactsUpdater:contactsUpdater
identityManager:identityManager
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
messageSender:messageSender];
}
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
storageManager:(TSStorageManager *)storageManager
callMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater
identityManager:(OWSIdentityManager *)identityManager
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
messageSender:(OWSMessageSender *)messageSender
{
2015-12-07 03:31:43 +01:00
self = [super init];
if (!self) {
return self;
2015-12-07 03:31:43 +01:00
}
_storageManager = storageManager;
_networkManager = networkManager;
_callMessageHandler = callMessageHandler;
_contactsManager = contactsManager;
_contactsUpdater = contactsUpdater;
_identityManager = identityManager;
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
_messageSender = messageSender;
_dbConnection = storageManager.newDatabaseConnection;
2017-02-16 00:32:27 +01:00
_incomingMessageFinder = [[OWSIncomingMessageFinder alloc] initWithDatabase:storageManager.database];
2017-04-03 20:42:04 +02:00
_blockingManager = [OWSBlockingManager sharedManager];
OWSSingletonAssert();
[self startObserving];
2015-12-07 03:31:43 +01:00
return self;
}
- (void)startObserving
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(yapDatabaseModified:)
name:YapDatabaseModifiedNotification
object:nil];
}
- (void)yapDatabaseModified:(NSNotification *)notification
{
[self updateApplicationBadgeCount];
}
#pragma mark - Debugging
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
- (NSString *)descriptionForEnvelopeType:(OWSSignalServiceProtosEnvelope *)envelope
{
OWSAssert(envelope != nil);
switch (envelope.type) {
case OWSSignalServiceProtosEnvelopeTypeReceipt:
return @"DeliveryReceipt";
case OWSSignalServiceProtosEnvelopeTypeUnknown:
// Shouldn't happen
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeUnknown]);
return @"Unknown";
case OWSSignalServiceProtosEnvelopeTypeCiphertext:
return @"SignalEncryptedMessage";
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
// Unsupported
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeKeyExchange]);
return @"KeyExchange";
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle:
return @"PreKeyEncryptedMessage";
default:
// Shouldn't happen
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeOther]);
return @"Other";
}
}
- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
{
OWSAssert(envelope != nil);
return [NSString stringWithFormat:@"<Envelope type: %@, source: %@, timestamp: %llu content.length: %lu />",
[self descriptionForEnvelopeType:envelope],
envelopeAddress(envelope),
envelope.timestamp,
(unsigned long)envelope.content.length];
}
/**
* We don't want to just log `content.description` because we'd potentially log message bodies for dataMesssages and
* sync transcripts
*/
- (NSString *)descriptionForContent:(OWSSignalServiceProtosContent *)content
{
if (content.hasSyncMessage) {
return [NSString stringWithFormat:@"<SyncMessage: %@ />", [self descriptionForSyncMessage:content.syncMessage]];
} else if (content.hasDataMessage) {
return [NSString stringWithFormat:@"<DataMessage: %@ />", [self descriptionForDataMessage:content.dataMessage]];
} else if (content.hasCallMessage) {
return [NSString stringWithFormat:@"<CallMessage: %@ />", content.callMessage];
} else if (content.hasNullMessage) {
return [NSString stringWithFormat:@"<NullMessage: %@ />", content.nullMessage];
} else {
// 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";
}
}
/**
* We don't want to just log `dataMessage.description` because we'd potentially log message contents
*/
- (NSString *)descriptionForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
NSMutableString *description = [NSMutableString new];
if (dataMessage.hasGroup) {
[description appendString:@"(Group:YES) "];
}
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
[description appendString:@"EndSession"];
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
[description appendString:@"ExpirationTimerUpdate"];
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsProfileKey) != 0) {
[description appendString:@"ProfileKey"];
} else if (dataMessage.attachments.count > 0) {
[description appendString:@"MessageWithAttachment"];
} else {
[description appendString:@"Plain"];
}
return [NSString stringWithFormat:@"<%@ />", description];
}
/**
* We don't want to just log `syncMessage.description` because we'd potentially log message contents in sent transcripts
*/
- (NSString *)descriptionForSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
{
NSMutableString *description = [NSMutableString new];
if (syncMessage.hasSent) {
[description appendString:@"SentTranscript"];
} else if (syncMessage.hasRequest) {
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
[description appendString:@"ContactRequest"];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
[description appendString:@"GroupRequest"];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeBlocked) {
[description appendString:@"BlockedRequest"];
} else {
// Shouldn't happen
OWSFail(@"Unknown sync message request type");
[description appendString:@"UnknownRequest"];
}
} else if (syncMessage.hasBlocked) {
[description appendString:@"Blocked"];
} else if (syncMessage.read.count > 0) {
[description appendString:@"ReadReceipt"];
} else if (syncMessage.hasVerified) {
NSString *verifiedString =
[NSString stringWithFormat:@"Verification for: %@", syncMessage.verified.destination];
[description appendString:verifiedString];
} else {
// Shouldn't happen
OWSFail(@"Unknown sync message type");
[description appendString:@"Unknown"];
}
return description;
}
#pragma mark - Blocking
- (BOOL)isEnvelopeBlocked:(OWSSignalServiceProtosEnvelope *)envelope
{
OWSAssert(envelope);
return [_blockingManager.blockedPhoneNumbers containsObject:envelope.source];
}
#pragma mark - Decryption
- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
successBlock:(DecryptSuccessBlock)successBlockParameter
failureBlock:(DecryptFailureBlock)failureBlockParameter
{
OWSAssert(envelope);
OWSAssert(successBlockParameter);
OWSAssert(failureBlockParameter);
2017-08-04 18:01:45 +02:00
OWSAssert([TSAccountManager isRegistered]);
// Ensure that successBlock and failureBlock are called on a worker queue.
DecryptSuccessBlock successBlock = ^(NSData *_Nullable plaintextData) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
successBlockParameter(plaintextData);
});
};
DecryptFailureBlock failureBlock = ^() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
failureBlockParameter();
});
};
DDLogInfo(@"%@ decrypting envelope: %@", self.tag, [self descriptionForEnvelope:envelope]);
OWSAssert(envelope.source.length > 0);
if ([self isEnvelopeBlocked:envelope]) {
DDLogInfo(@"%@ ignoring blocked envelope: %@", self.tag, envelope.source);
failureBlock();
return;
}
2015-12-07 03:31:43 +01:00
@try {
switch (envelope.type) {
case OWSSignalServiceProtosEnvelopeTypeCiphertext: {
[self decryptSecureMessage:envelope
successBlock:^(NSData *_Nullable plaintextData) {
DDLogDebug(@"%@ decrypted secure message.", self.tag);
successBlock(plaintextData);
}
failureBlock:^(NSError *_Nullable error) {
DDLogError(@"%@ decrypting secure message from address: %@ failed with error: %@",
self.tag,
envelopeAddress(envelope),
error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]);
failureBlock();
}];
// Return to avoid double-acknowledging.
return;
}
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: {
[self decryptPreKeyBundle:envelope
successBlock:^(NSData *_Nullable plaintextData) {
DDLogDebug(@"%@ decrypted pre-key whisper message", self.tag);
successBlock(plaintextData);
}
failureBlock:^(NSError *_Nullable error) {
DDLogError(@"%@ decrypting pre-key whisper message from address: %@ failed "
@"with error: %@",
self.tag,
envelopeAddress(envelope),
error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]);
failureBlock();
}];
// Return to avoid double-acknowledging.
return;
}
2017-09-14 16:36:13 +02:00
// These message types don't have a payload to decrypt.
case OWSSignalServiceProtosEnvelopeTypeReceipt:
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
case OWSSignalServiceProtosEnvelopeTypeUnknown:
successBlock(nil);
// Return to avoid double-acknowledging.
return;
2015-12-07 03:31:43 +01:00
default:
2016-09-01 21:42:51 +02:00
DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type);
2015-12-07 03:31:43 +01:00
break;
}
} @catch (NSException *exception) {
DDLogError(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription);
OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]);
2015-12-07 03:31:43 +01:00
}
failureBlock();
2015-12-07 03:31:43 +01:00
}
2017-09-14 16:36:13 +02:00
- (void)decryptSecureMessage:(OWSSignalServiceProtosEnvelope *)envelope
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
2017-09-14 16:36:13 +02:00
OWSAssert(envelope);
OWSAssert(successBlock);
OWSAssert(failureBlock);
2017-08-04 18:01:45 +02:00
2017-09-14 16:36:13 +02:00
[self decryptEnvelope:envelope
messageTypeName:@"Secure Message"
missingPayloadBlock:^{
OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]);
}
2017-09-14 16:36:13 +02:00
cipherMessageBlock:^(NSData *encryptedData) {
return [[WhisperMessage alloc] initWithData:encryptedData];
}
2017-09-14 16:36:13 +02:00
successBlock:successBlock
failureBlock:failureBlock];
2015-12-07 03:31:43 +01:00
}
2017-09-14 16:36:13 +02:00
- (void)decryptPreKeyBundle:(OWSSignalServiceProtosEnvelope *)envelope
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
2017-09-14 16:36:13 +02:00
OWSAssert(envelope);
OWSAssert(successBlock);
OWSAssert(failureBlock);
// Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage.
[TSPreKeyManager checkPreKeys];
[self decryptEnvelope:envelope
messageTypeName:@"PreKey Bundle"
missingPayloadBlock:^{
OWSProdFail([OWSAnalyticsEvents messageManagerErrorPrekeyBundleEnvelopeHasNoContent]);
}
cipherMessageBlock:^(NSData *encryptedData) {
return [[PreKeyWhisperMessage alloc] initWithData:encryptedData];
}
successBlock:successBlock
failureBlock:failureBlock];
}
- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
messageTypeName:(NSString *)messageTypeName
missingPayloadBlock:(void (^_Nonnull)())missingPayloadBlock
cipherMessageBlock:(id<CipherMessage> (^_Nonnull)(NSData *))cipherMessageBlock
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
OWSAssert(envelope);
OWSAssert(messageTypeName.length > 0);
OWSAssert(missingPayloadBlock);
OWSAssert(cipherMessageBlock);
OWSAssert(successBlock);
OWSAssert(failureBlock);
2015-12-07 03:31:43 +01:00
TSStorageManager *storageManager = [TSStorageManager sharedManager];
2017-09-14 16:36:13 +02:00
NSString *recipientId = envelope.source;
int deviceId = envelope.sourceDevice;
// DEPRECATED - Remove after all clients have been upgraded.
2017-09-14 16:36:13 +02:00
NSData *encryptedData = envelope.hasContent ? envelope.content : envelope.legacyMessage;
if (!encryptedData) {
2017-09-14 16:36:13 +02:00
missingPayloadBlock();
failureBlock(nil);
return;
}
dispatch_async([OWSDispatch sessionStoreQueue], ^{
@try {
2017-09-14 16:36:13 +02:00
id<CipherMessage> cipherMessage = cipherMessageBlock(encryptedData);
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
preKeyStore:storageManager
signedPreKeyStore:storageManager
identityKeyStore:self.identityManager
recipientId:recipientId
deviceId:deviceId];
2017-09-14 16:36:13 +02:00
NSData *plaintextData = [[cipher decrypt:cipherMessage] removePadding];
successBlock(plaintextData);
} @catch (NSException *exception) {
dispatch_async(dispatch_get_main_queue(), ^{
2017-09-14 16:36:13 +02:00
[self processException:exception envelope:envelope];
NSString *errorDescription = [NSString
stringWithFormat:@"Exception while decrypting %@: %@", messageTypeName, exception.description];
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error);
});
}
});
2015-12-07 03:31:43 +01:00
}
#pragma mark - message handling
- (void)processEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
plaintextData:(NSData *_Nullable)plaintextData
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(transaction);
OWSAssert([TSAccountManager isRegistered]);
2017-02-10 01:35:10 +01:00
DDLogInfo(@"%@ received envelope: %@", self.tag, [self descriptionForEnvelope:envelope]);
2015-12-07 03:31:43 +01:00
OWSAssert(envelope.source.length > 0);
OWSAssert(![self isEnvelopeBlocked:envelope]);
2015-12-07 03:31:43 +01:00
switch (envelope.type) {
case OWSSignalServiceProtosEnvelopeTypeCiphertext:
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle:
if (plaintextData) {
[self handleEnvelope:envelope plaintextData:plaintextData transaction:transaction];
} else {
OWSFail(
@"%@ missing decrypted data for envelope: %@", self.tag, [self descriptionForEnvelope:envelope]);
}
break;
case OWSSignalServiceProtosEnvelopeTypeReceipt:
OWSAssert(!plaintextData);
[self handleDeliveryReceipt:envelope transaction:transaction];
break;
// Other messages are just dismissed for now.
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
DDLogWarn(@"Received Key Exchange Message, not supported");
break;
case OWSSignalServiceProtosEnvelopeTypeUnknown:
DDLogWarn(@"Received an unknown message type");
break;
default:
DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type);
break;
}
}
- (void)handleDeliveryReceipt:(OWSSignalServiceProtosEnvelope *)envelope
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(transaction);
TSInteraction *interaction = [TSInteraction interactionForTimestamp:envelope.timestamp withTransaction:transaction];
if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)interaction;
[outgoingMessage updateWithWasDeliveredWithTransaction:transaction];
}
}
- (void)handleEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
plaintextData:(NSData *)plaintextData
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(plaintextData);
OWSAssert(transaction);
OWSAssert(envelope.hasTimestamp && envelope.timestamp > 0);
OWSAssert(envelope.hasSource && envelope.source.length > 0);
OWSAssert(envelope.hasSourceDevice && envelope.sourceDevice > 0);
2017-02-16 00:32:27 +01:00
BOOL duplicateEnvelope = [self.incomingMessageFinder existsMessageWithTimestamp:envelope.timestamp
sourceId:envelope.source
sourceDeviceId:envelope.sourceDevice
transaction:transaction];
2017-02-16 00:32:27 +01:00
if (duplicateEnvelope) {
DDLogInfo(@"%@ Ignoring previously received envelope from %@ with timestamp: %llu",
self.tag,
envelopeAddress(envelope),
envelope.timestamp);
2017-02-16 00:32:27 +01:00
return;
}
if (envelope.hasContent) {
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
DDLogInfo(@"%@ handling content: <Content: %@>", self.tag, [self descriptionForContent:content]);
if (content.hasSyncMessage) {
[self handleIncomingEnvelope:envelope withSyncMessage:content.syncMessage transaction:transaction];
} else if (content.hasDataMessage) {
[self handleIncomingEnvelope:envelope withDataMessage:content.dataMessage transaction:transaction];
} else if (content.hasCallMessage) {
[self handleIncomingEnvelope:envelope withCallMessage:content.callMessage];
} else if (content.hasNullMessage) {
DDLogInfo(@"%@ Received null message.", self.tag);
} else {
2017-02-16 00:32:27 +01:00
DDLogWarn(@"%@ Ignoring envelope. Content with no known payload", self.tag);
}
} else if (envelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded.
OWSSignalServiceProtosDataMessage *dataMessage =
[OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
DDLogInfo(@"%@ handling message: <DataMessage: %@ />", self.tag, [self descriptionForDataMessage:dataMessage]);
[self handleIncomingEnvelope:envelope withDataMessage:dataMessage transaction:transaction];
} else {
2017-08-04 18:01:45 +02:00
OWSProdInfoWEnvelope([OWSAnalyticsEvents messageManagerErrorEnvelopeNoActionablePayload], envelope);
2015-12-07 03:31:43 +01:00
}
}
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
if ([dataMessage hasProfileKey]) {
NSData *profileKey = [dataMessage profileKey];
NSString *recipientId = envelope.source;
if (profileKey.length == kAES256_KeyByteLength) {
[self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId];
} else {
OWSFail(
@"Unexpected profile key length:%lu on message from:%@", (unsigned long)profileKey.length, recipientId);
}
}
if (dataMessage.hasGroup) {
TSGroupModel *emptyModelToFillOutId =
[[TSGroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:dataMessage.group.id];
TSGroupThread *gThread = [TSGroupThread threadWithGroupModel:emptyModelToFillOutId transaction:transaction];
BOOL unknownGroup = NO;
if (gThread == nil && dataMessage.group.type != OWSSignalServiceProtosGroupContextTypeUpdate) {
unknownGroup = YES;
}
if (unknownGroup) {
if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo) {
DDLogInfo(
@"%@ Ignoring group info request for group I don't know about from: %@", self.tag, envelope.source);
return;
}
// FIXME: https://github.com/WhisperSystems/Signal-iOS/issues/1340
DDLogInfo(@"%@ Received message from group that I left or don't know about from: %@",
self.tag,
envelopeAddress(envelope));
NSString *recipientId = envelope.source;
TSThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction];
NSData *groupId = dataMessage.group.id;
OWSAssert(groupId);
OWSSyncGroupsRequestMessage *syncGroupsRequestMessage =
[[OWSSyncGroupsRequestMessage alloc] initWithThread:thread groupId:groupId];
[self.messageSender sendMessage:syncGroupsRequestMessage
transaction:transaction
success:^{
DDLogWarn(@"%@ Successfully sent Request Group Info message.", self.tag);
}
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send Request Group Info message with error: %@", self.tag, error);
}];
2015-12-07 03:31:43 +01:00
}
}
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
[self handleEndSessionMessageWithEnvelope:envelope dataMessage:dataMessage transaction:transaction];
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
[self handleExpirationTimerUpdateMessageWithEnvelope:envelope dataMessage:dataMessage transaction:transaction];
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsProfileKey) != 0) {
[self handleProfileKeyMessageWithEnvelope:envelope dataMessage:dataMessage];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
} else if (dataMessage.attachments.count > 0) {
[self handleReceivedMediaWithEnvelope:envelope dataMessage:dataMessage transaction:transaction];
2015-12-07 03:31:43 +01:00
} else {
[self handleReceivedTextMessageWithEnvelope:envelope dataMessage:dataMessage transaction:transaction];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
if ([self isDataMessageGroupAvatarUpdate:dataMessage]) {
DDLogVerbose(@"%@ Data message had group avatar attachment", self.tag);
[self handleReceivedGroupAvatarUpdateWithEnvelope:envelope dataMessage:dataMessage transaction:transaction];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
}
}
}
- (id<ProfileManagerProtocol>)profileManager
{
return [TextSecureKitEnv sharedEnv].profileManager;
}
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withCallMessage:(OWSSignalServiceProtosCallMessage *)callMessage
{
OWSAssert(envelope);
OWSAssert(callMessage);
if ([callMessage hasProfileKey]) {
NSData *profileKey = [callMessage profileKey];
NSString *recipientId = envelope.source;
[self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId];
}
// TODO: Should we do this synchronously?
dispatch_async(dispatch_get_main_queue(), ^{
if (callMessage.hasOffer) {
[self.callMessageHandler receivedOffer:callMessage.offer fromCallerId:envelope.source];
} else if (callMessage.hasAnswer) {
[self.callMessageHandler receivedAnswer:callMessage.answer fromCallerId:envelope.source];
} else if (callMessage.iceUpdate.count > 0) {
for (OWSSignalServiceProtosCallMessageIceUpdate *iceUpdate in callMessage.iceUpdate) {
[self.callMessageHandler receivedIceUpdate:iceUpdate fromCallerId:envelope.source];
}
} else if (callMessage.hasHangup) {
DDLogVerbose(@"%@ Received CallMessage with Hangup.", self.tag);
[self.callMessageHandler receivedHangup:callMessage.hangup fromCallerId:envelope.source];
} else if (callMessage.hasBusy) {
[self.callMessageHandler receivedBusy:callMessage.busy fromCallerId:envelope.source];
} else {
OWSProdInfoWEnvelope([OWSAnalyticsEvents messageManagerErrorCallMessageNoActionablePayload], envelope);
}
});
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
- (void)handleReceivedGroupAvatarUpdateWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
TSGroupThread *groupThread =
[TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id transaction:transaction];
2017-08-04 18:01:45 +02:00
OWSAssert(groupThread);
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ]
timestamp:envelope.timestamp
relay:envelope.relay
thread:groupThread
networkManager:self.networkManager
storageManager:self.storageManager
transaction:transaction];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
if (!attachmentsProcessor.hasSupportedAttachments) {
DDLogWarn(@"%@ received unsupported group avatar envelope", self.tag);
return;
2015-12-07 03:31:43 +01:00
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
[attachmentsProcessor fetchAttachmentsForMessage:nil
transaction:transaction
2017-05-10 16:05:01 +02:00
success:^(TSAttachmentStream *attachmentStream) {
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
[groupThread updateAvatarWithAttachmentStream:attachmentStream];
}
2017-05-10 16:05:01 +02:00
failure:^(NSError *error) {
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
DDLogError(@"%@ failed to fetch attachments for group avatar sent at: %llu. with error: %@",
self.tag,
envelope.timestamp,
error);
}];
}
- (void)handleReceivedMediaWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction];
2017-08-04 18:01:45 +02:00
OWSAssert(thread);
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments
timestamp:envelope.timestamp
relay:envelope.relay
thread:thread
networkManager:self.networkManager
storageManager:self.storageManager
transaction:transaction];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
if (!attachmentsProcessor.hasSupportedAttachments) {
DDLogWarn(@"%@ received unsupported media envelope", self.tag);
return;
}
2017-05-10 16:05:01 +02:00
TSIncomingMessage *_Nullable createdMessage =
[self handleReceivedEnvelope:envelope
withDataMessage:dataMessage
attachmentIds:attachmentsProcessor.supportedAttachmentIds
transaction:transaction];
2017-05-10 16:05:01 +02:00
if (!createdMessage) {
return;
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
DDLogDebug(@"%@ incoming attachment message: %@", self.tag, createdMessage.debugDescription);
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
[attachmentsProcessor fetchAttachmentsForMessage:createdMessage
transaction:transaction
2017-05-10 16:05:01 +02:00
success:^(TSAttachmentStream *attachmentStream) {
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
DDLogDebug(
@"%@ successfully fetched attachment: %@ for message: %@", self.tag, attachmentStream, createdMessage);
}
2017-05-10 16:05:01 +02:00
failure:^(NSError *error) {
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
DDLogError(
@"%@ failed to fetch attachments for message: %@ with error: %@", self.tag, createdMessage, error);
}];
2015-12-07 03:31:43 +01:00
}
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(syncMessage);
OWSAssert(transaction);
2017-08-04 18:01:45 +02:00
OWSAssert([TSAccountManager isRegistered]);
2017-08-04 18:01:45 +02:00
NSString *localNumber = [TSAccountManager localNumber];
if (![localNumber isEqualToString:envelope.source]) {
2017-08-04 18:01:45 +02:00
// Sync messages should only come from linked devices.
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorSyncMessageFromUnknownSource], envelope);
2017-08-04 18:01:45 +02:00
return;
}
if (syncMessage.hasSent) {
OWSIncomingSentMessageTranscript *transcript =
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:envelope.relay];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
OWSRecordTranscriptJob *recordJob =
[[OWSRecordTranscriptJob alloc] initWithIncomingSentMessageTranscript:transcript
messageSender:self.messageSender
networkManager:self.networkManager];
OWSSignalServiceProtosDataMessage *dataMessage = syncMessage.sent.message;
OWSAssert(dataMessage);
NSString *destination = syncMessage.sent.destination;
if (dataMessage && destination.length > 0 && dataMessage.hasProfileKey) {
// If we observe a linked device sending our profile key to another
// user, we can infer that that user belongs in our profile whitelist.
if (dataMessage.hasGroup) {
[self.profileManager addGroupIdToProfileWhitelist:dataMessage.group.id];
} else {
[self.profileManager addUserToProfileWhitelist:destination];
}
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
if ([self isDataMessageGroupAvatarUpdate:syncMessage.sent.message]) {
2017-05-10 16:05:01 +02:00
[recordJob runWithAttachmentHandler:^(TSAttachmentStream *attachmentStream) {
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
TSGroupThread *groupThread =
[TSGroupThread getOrCreateThreadWithGroupIdData:syncMessage.sent.message.group.id
transaction:transaction];
[groupThread updateAvatarWithAttachmentStream:attachmentStream transaction:transaction];
}
transaction:transaction];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
} else {
2017-05-10 16:05:01 +02:00
[recordJob runWithAttachmentHandler:^(TSAttachmentStream *attachmentStream) {
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
DDLogDebug(@"%@ successfully fetched transcript attachment: %@", self.tag, attachmentStream);
}
transaction:transaction];
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
}
2016-09-01 16:28:35 +02:00
} else if (syncMessage.hasRequest) {
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
OWSSyncContactsMessage *syncContactsMessage =
[[OWSSyncContactsMessage alloc] initWithSignalAccounts:self.contactsManager.signalAccounts
identityManager:self.identityManager
profileManager:self.profileManager];
2017-09-18 21:02:34 +02:00
DataSource *dataSource =
[DataSourceValue dataSourceWithSyncMessage:[syncContactsMessage buildPlainTextAttachmentData]];
[self.messageSender sendTemporaryAttachmentData:dataSource
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncContactsMessage
success:^{
DDLogInfo(@"%@ Successfully sent Contacts response syncMessage.", self.tag);
}
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send Contacts response syncMessage with error: %@", self.tag, error);
}];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
2017-09-18 21:02:34 +02:00
DataSource *dataSource =
[DataSourceValue dataSourceWithSyncMessage:[syncGroupsMessage buildPlainTextAttachmentData]];
[self.messageSender sendTemporaryAttachmentData:dataSource
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncGroupsMessage
success:^{
DDLogInfo(@"%@ Successfully sent Groups response syncMessage.", self.tag);
}
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send Groups response syncMessage with error: %@", self.tag, error);
}];
} else {
DDLogWarn(@"%@ ignoring unsupported sync request message", self.tag);
}
2017-03-31 02:04:19 +02:00
} else if (syncMessage.hasBlocked) {
dispatch_async(dispatch_get_main_queue(), ^{
NSArray<NSString *> *blockedPhoneNumbers = [syncMessage.blocked.numbers copy];
[_blockingManager setBlockedPhoneNumbers:blockedPhoneNumbers sendSyncMessage:NO];
});
2016-09-01 16:28:35 +02:00
} else if (syncMessage.read.count > 0) {
DDLogInfo(@"%@ Received %ld read receipt(s)", self.tag, (u_long)syncMessage.read.count);
2016-09-01 16:28:35 +02:00
OWSReadReceiptsProcessor *readReceiptsProcessor =
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read
storageManager:self.storageManager];
[readReceiptsProcessor processWithTransaction:transaction];
} else if (syncMessage.hasVerified) {
DDLogInfo(@"%@ Received verification state for %@", self.tag, syncMessage.verified.destination);
dispatch_async(dispatch_get_main_queue(), ^{
[self.identityManager processIncomingSyncMessage:syncMessage.verified];
});
} else {
DDLogWarn(@"%@ Ignoring unsupported sync message.", self.tag);
}
}
- (void)handleEndSessionMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
if (thread) { // TODO thread should always be nonnull.
[[[TSInfoMessage alloc] initWithTimestamp:envelope.timestamp
inThread:thread
messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction];
}
2015-12-07 03:31:43 +01:00
dispatch_async([OWSDispatch sessionStoreQueue], ^{
[[TSStorageManager sharedManager] deleteAllSessionsForContact:envelope.source];
});
2015-12-07 03:31:43 +01:00
}
- (void)handleExpirationTimerUpdateMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction];
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
if (dataMessage.hasExpireTimer && dataMessage.expireTimer > 0) {
DDLogInfo(@"%@ Expiring messages duration turned to %u for thread %@",
self.tag,
(unsigned int)dataMessage.expireTimer,
thread);
disappearingMessagesConfiguration =
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
enabled:YES
durationSeconds:dataMessage.expireTimer];
} else {
DDLogInfo(@"%@ Expiring messages have been turned off for thread %@", self.tag, thread);
disappearingMessagesConfiguration = [[OWSDisappearingMessagesConfiguration alloc]
initWithThreadId:thread.uniqueId
enabled:NO
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
}
2017-08-04 18:01:45 +02:00
OWSAssert(disappearingMessagesConfiguration);
[disappearingMessagesConfiguration saveWithTransaction:transaction];
NSString *name = [self.contactsManager displayNameForPhoneIdentifier:envelope.source];
OWSDisappearingConfigurationUpdateInfoMessage *message =
[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:envelope.timestamp
thread:thread
configuration:disappearingMessagesConfiguration
createdByRemoteName:name];
[message saveWithTransaction:transaction];
2015-12-07 03:31:43 +01:00
}
- (void)handleProfileKeyMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
OWSAssert(envelope);
OWSAssert(dataMessage);
NSString *recipientId = envelope.source;
if (!dataMessage.hasProfileKey) {
OWSFail(@"%@ received profile key message without profile key from: %@", self.tag, envelopeAddress(envelope));
return;
}
NSData *profileKey = dataMessage.profileKey;
if (profileKey.length != kAES256_KeyByteLength) {
2017-08-29 00:03:14 +02:00
OWSFail(@"%@ received profile key of unexpected length:%lu from:%@",
self.tag,
(unsigned long)profileKey.length,
envelopeAddress(envelope));
return;
}
id<ProfileManagerProtocol> profileManager = [TextSecureKitEnv sharedEnv].profileManager;
[profileManager setProfileKeyData:profileKey forRecipientId:recipientId];
}
- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
[self handleReceivedEnvelope:envelope withDataMessage:dataMessage attachmentIds:@[] transaction:transaction];
2015-12-07 03:31:43 +01:00
}
- (void)sendGroupUpdateForThread:(TSGroupThread *)gThread message:(TSOutgoingMessage *)message
{
OWSAssert([NSThread isMainThread]);
OWSAssert(gThread);
2017-08-04 18:01:45 +02:00
OWSAssert(gThread.groupModel);
OWSAssert(message);
if (gThread.groupModel.groupImage) {
NSData *data = UIImagePNGRepresentation(gThread.groupModel.groupImage);
2017-09-18 21:02:34 +02:00
DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithData:data fileExtension:@"png"];
[self.messageSender sendAttachmentData:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
success:^{
DDLogDebug(@"%@ Successfully sent group update with avatar", self.tag);
}
2017-05-10 16:05:01 +02:00
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send group avatar update with error: %@", self.tag, error);
}];
} else {
[self.messageSender sendMessage:message
success:^{
DDLogDebug(@"%@ Successfully sent group update", self.tag);
}
2017-05-10 16:05:01 +02:00
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send group update with error: %@", self.tag, error);
}];
}
}
- (void)handleGroupInfoRequest:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
2017-05-10 16:05:01 +02:00
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
2017-05-10 16:05:01 +02:00
OWSAssert(dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo);
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
if (!groupId) {
OWSFail(@"Group info request is missing group id.");
2017-05-10 16:05:01 +02:00
return;
}
DDLogWarn(@"%@ Received 'Request Group Info' message for group: %@ from: %@", self.tag, groupId, envelope.source);
2017-05-10 16:05:01 +02:00
TSGroupModel *emptyModelToFillOutId =
[[TSGroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:dataMessage.group.id];
TSGroupThread *gThread = [TSGroupThread threadWithGroupModel:emptyModelToFillOutId transaction:transaction];
if (!gThread) {
DDLogWarn(@"%@ Unknown group: %@", self.tag, groupId);
return;
}
if (![gThread.groupModel.groupMemberIds containsObject:envelope.source]) {
DDLogWarn(@"%@ Ignoring 'Request Group Info' message for non-member of group. %@ not in %@",
self.tag,
envelope.source,
gThread.groupModel.groupMemberIds);
}
2017-05-10 16:05:01 +02:00
NSString *updateGroupInfo =
[gThread.groupModel getInfoStringAboutUpdateTo:gThread.groupModel contactsManager:self.contactsManager];
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:gThread
groupMetaMessage:TSGroupMessageUpdate];
[message updateWithCustomMessage:updateGroupInfo transaction:transaction];
// Only send this group update to the requester.
[message updateWithSingleGroupRecipient:envelope.source transaction:transaction];
dispatch_async(dispatch_get_main_queue(), ^{
[self sendGroupUpdateForThread:gThread message:message];
});
2017-05-10 16:05:01 +02:00
}
- (TSIncomingMessage *_Nullable)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
uint64_t timestamp = envelope.timestamp;
NSString *body = dataMessage.body;
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
2015-12-07 03:31:43 +01:00
__block TSIncomingMessage *_Nullable incomingMessage;
__block TSThread *thread;
// Do this outside of a transaction to avoid deadlock
OWSAssert([TSAccountManager isRegistered]);
NSString *localNumber = [TSAccountManager localNumber];
2017-05-10 16:05:01 +02:00
if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo) {
[self handleGroupInfoRequest:envelope dataMessage:dataMessage transaction:transaction];
2017-05-10 16:05:01 +02:00
return nil;
}
if (groupId) {
NSMutableArray *uniqueMemberIds = [[[NSSet setWithArray:dataMessage.group.members] allObjects] mutableCopy];
TSGroupModel *model = [[TSGroupModel alloc] initWithTitle:dataMessage.group.name
memberIds:uniqueMemberIds
image:nil
groupId:dataMessage.group.id];
TSGroupThread *gThread = [TSGroupThread getOrCreateThreadWithGroupModel:model transaction:transaction];
[gThread saveWithTransaction:transaction];
switch (dataMessage.group.type) {
case OWSSignalServiceProtosGroupContextTypeUpdate: {
NSString *updateGroupInfo =
[gThread.groupModel getInfoStringAboutUpdateTo:model contactsManager:self.contactsManager];
gThread.groupModel = model;
[gThread saveWithTransaction:transaction];
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
inThread:gThread
messageType:TSInfoMessageTypeGroupUpdate
customMessage:updateGroupInfo] saveWithTransaction:transaction];
break;
}
case OWSSignalServiceProtosGroupContextTypeQuit: {
NSString *nameString = [self.contactsManager displayNameForPhoneIdentifier:envelope.source];
NSString *updateGroupInfo =
[NSString stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_LEFT", @""), nameString];
NSMutableArray *newGroupMembers = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds];
[newGroupMembers removeObject:envelope.source];
gThread.groupModel.groupMemberIds = newGroupMembers;
[gThread saveWithTransaction:transaction];
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
inThread:gThread
messageType:TSInfoMessageTypeGroupUpdate
customMessage:updateGroupInfo] saveWithTransaction:transaction];
break;
}
case OWSSignalServiceProtosGroupContextTypeDeliver: {
if (body.length == 0 && attachmentIds.count < 1) {
DDLogWarn(@"%@ ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu",
self.tag,
envelopeAddress(envelope),
groupId,
(unsigned long)timestamp);
} else {
DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu",
self.tag,
envelopeAddress(envelope),
groupId,
(unsigned long)timestamp);
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
inThread:gThread
authorId:envelope.source
sourceDeviceId:envelope.sourceDevice
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
[incomingMessage saveWithTransaction:transaction];
}
break;
}
default: {
DDLogWarn(@"%@ Ignoring unknown group message type: %d", self.tag, (int)dataMessage.group.type);
}
}
thread = gThread;
} else {
if (body.length == 0 && attachmentIds.count < 1) {
DDLogWarn(@"%@ ignoring empty incoming message from: %@ with timestamp: %lu",
self.tag,
envelopeAddress(envelope),
(unsigned long)timestamp);
} else {
DDLogDebug(@"%@ incoming message from: %@ with timestamp: %lu",
self.tag,
envelopeAddress(envelope),
(unsigned long)timestamp);
TSContactThread *cThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source
transaction:transaction
relay:envelope.relay];
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
inThread:cThread
authorId:[cThread contactIdentifier]
sourceDeviceId:envelope.sourceDevice
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
[incomingMessage saveWithTransaction:transaction];
thread = cThread;
}
}
2015-12-07 03:31:43 +01:00
if (thread && incomingMessage) {
// Any messages sent from the current user - from this device or another - should be
// automatically marked as read.
BOOL shouldMarkMessageAsRead = [envelope.source isEqualToString:localNumber];
if (shouldMarkMessageAsRead) {
// Don't send a read receipt for messages sent by ourselves.
[incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:YES];
}
DDLogDebug(@"%@ shouldMarkMessageAsRead: %d (%@)", self.tag, shouldMarkMessageAsRead, envelope.source);
// Other clients allow attachments to be sent along with body, we want the text displayed as a separate
// message
if ([attachmentIds count] > 0 && body != nil && body.length > 0) {
// We want the text to be displayed under the attachment
uint64_t textMessageTimestamp = timestamp + 1;
TSIncomingMessage *textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:thread
authorId:envelope.source
sourceDeviceId:envelope.sourceDevice
messageBody:body
attachmentIds:@[]
expiresInSeconds:dataMessage.expireTimer];
DDLogDebug(@"%@ incoming extra text message: %@", self.tag, incomingMessage.debugDescription);
[textMessage saveWithTransaction:transaction];
}
}
if (thread && incomingMessage) {
dispatch_async(dispatch_get_main_queue(), ^{
// In case we already have a read receipt for this new message (happens sometimes).
OWSReadReceiptsProcessor *readReceiptsProcessor =
[[OWSReadReceiptsProcessor alloc] initWithIncomingMessage:incomingMessage
storageManager:self.storageManager];
[readReceiptsProcessor process];
[OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:incomingMessage
contactsManager:self.contactsManager];
// Update thread preview in inbox
[thread touch];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForIncomingMessage:incomingMessage
inThread:thread
contactsManager:self.contactsManager];
});
}
return incomingMessage;
2015-12-07 03:31:43 +01:00
}
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
{
OWSAssert([NSThread isMainThread]);
DDLogError(@"%@ Got exception: %@ of type: %@ with reason: %@",
self.tag,
exception.description,
exception.name,
exception.reason);
2015-12-07 03:31:43 +01:00
__block TSErrorMessage *errorMessage;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if ([exception.name isEqualToString:NoSessionException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope);
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKey], envelope);
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyIdException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKeyId], envelope);
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:DuplicateMessageException]) {
// Duplicate messages are dismissed
return;
} else if ([exception.name isEqualToString:InvalidVersionException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidMessageVersion], envelope);
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
// Should no longer get here, since we now record the new identity for incoming messages.
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorUntrustedIdentityKeyException], envelope);
OWSFail(@"%@ Failed to trust identity on incoming message from: %@", self.tag, envelopeAddress(envelope));
return;
} else {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope);
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
}
2015-12-07 03:31:43 +01:00
[errorMessage saveWithTransaction:transaction];
2015-12-07 03:31:43 +01:00
}];
if (errorMessage != nil) {
[self notifyForErrorMessage:errorMessage withEnvelope:envelope];
}
}
- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
{
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
2015-12-07 03:31:43 +01:00
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
#pragma mark - helpers
2015-12-07 03:31:43 +01:00
// used in log formatting
NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope)
{
return [NSString stringWithFormat:@"%@.%d", envelope.source, (unsigned int)envelope.sourceDevice];
}
Explain send failures for text and media messages Motivation ---------- We were often swallowing errors or yielding generic errors when it would be better to provide specific errors. We also didn't create an attachment when attachments failed to send, making it impossible to show the user what was happening with an in-progress or failed attachment. Primary Changes --------------- - Funnel all message sending through MessageSender, and remove message sending from MessagesManager. - Record most recent sending error so we can expose it in the UI - Can resend attachments. - Update message status for attachments, just like text messages - Extracted UploadingService from MessagesManager - Saving attachment stream before uploading gives uniform API for send vs. resend - update status for downloading transcript attachments - TSAttachments have a local id, separate from the server allocated id This allows us to save the attachment before the allocation request. Which is is good because: 1. can show feedback to user faster. 2. allows us to show an error when allocation fails. Code Cleanup ------------ - Replaced a lot of global singleton access with injected dependencies to make for easier testing. - Never save group meta messages. Rather than checking before (hopefully) every save, do it in the save method. - Don't use callbacks for sync code. - Handle errors on writing attachment data - Fix old long broken tests that weren't even running. =( - Removed dead code - Use constants vs define - Port flaky travis fixes from Signal-iOS // FREEBIE
2016-10-14 23:00:29 +02:00
- (BOOL)isDataMessageGroupAvatarUpdate:(OWSSignalServiceProtosDataMessage *)dataMessage
{
return dataMessage.hasGroup
&& dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
&& dataMessage.group.hasAvatar;
2015-12-07 03:31:43 +01:00
}
/**
* @returns
* Group or Contact thread for message, creating a new one if necessary.
*/
- (TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(envelope);
OWSAssert(dataMessage);
OWSAssert(transaction);
if (dataMessage.hasGroup) {
return [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id transaction:transaction];
} else {
return [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
}
}
2015-12-07 03:31:43 +01:00
- (NSUInteger)unreadMessagesCount {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
}];
return numberOfItems;
}
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
id databaseView = [transaction ext:TSUnreadDatabaseViewExtensionName];
OWSAssert(databaseView);
numberOfItems = ([databaseView numberOfItemsInAllGroups] - [databaseView numberOfItemsInGroup:thread.uniqueId]);
2015-12-07 03:31:43 +01:00
}];
return numberOfItems;
}
- (void)updateApplicationBadgeCount
{
NSUInteger numberOfItems = [self unreadMessagesCount];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:numberOfItems];
}
2015-12-07 03:31:43 +01:00
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
}];
return numberOfItems;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
2015-12-07 03:31:43 +01:00
@end
NS_ASSUME_NONNULL_END