session-ios/src/Messages/TSMessagesManager.m

692 lines
34 KiB
Mathematica
Raw Normal View History

2015-12-07 03:31:43 +01:00
// Created by Frederic Jacobs on 11/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSMessagesManager.h"
#import "ContactsManagerProtocol.h"
#import "ContactsUpdater.h"
#import "MimeTypeUtil.h"
2015-12-07 03:31:43 +01:00
#import "NSData+messagePadding.h"
#import "NSDate+millisecondTimeStamp.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"
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.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 "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"
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
@interface TSMessagesManager ()
@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;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@end
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;
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager
storageManager:storageManager
contactsManager:contactsManager
contactsUpdater:contactsUpdater];
return [self initWithNetworkManager:networkManager
storageManager:storageManager
contactsManager:contactsManager
contactsUpdater:contactsUpdater
messageSender:messageSender];
}
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
storageManager:(TSStorageManager *)storageManager
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater
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;
_contactsManager = contactsManager;
_contactsUpdater = contactsUpdater;
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;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
2015-12-07 03:31:43 +01:00
return self;
}
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 - message handling
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
{
2015-12-07 03:31:43 +01:00
@try {
switch (envelope.type) {
case OWSSignalServiceProtosEnvelopeTypeCiphertext:
[self handleSecureMessage:envelope];
2015-12-07 03:31:43 +01:00
break;
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle:
[self handlePreKeyBundle:envelope];
break;
case OWSSignalServiceProtosEnvelopeTypeReceipt:
DDLogInfo(@"Received a delivery receipt");
[self handleDeliveryReceipt:envelope];
2015-12-07 03:31:43 +01:00
break;
// Other messages are just dismissed for now.
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
2015-12-07 03:31:43 +01:00
DDLogWarn(@"Received Key Exchange Message, not supported");
break;
case OWSSignalServiceProtosEnvelopeTypeUnknown:
2015-12-07 03:31:43 +01:00
DDLogWarn(@"Received an unknown message type");
break;
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) {
DDLogWarn(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription);
}
}
- (void)handleDeliveryReceipt:(OWSSignalServiceProtosEnvelope *)envelope
{
2015-12-07 03:31:43 +01:00
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSInteraction *interaction =
[TSInteraction interactionForTimestamp:envelope.timestamp withTransaction:transaction];
if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)interaction;
outgoingMessage.messageState = TSOutgoingMessageStateDelivered;
2015-12-07 03:31:43 +01:00
[outgoingMessage saveWithTransaction:transaction];
}
2015-12-07 03:31:43 +01:00
}];
}
- (void)handleSecureMessage:(OWSSignalServiceProtosEnvelope *)messageEnvelope
{
2015-12-07 03:31:43 +01:00
@synchronized(self) {
TSStorageManager *storageManager = [TSStorageManager sharedManager];
NSString *recipientId = messageEnvelope.source;
int deviceId = messageEnvelope.sourceDevice;
2015-12-07 03:31:43 +01:00
if (![storageManager containsSession:recipientId deviceId:deviceId]) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage =
[TSErrorMessage missingSessionWithEnvelope:messageEnvelope withTransaction:transaction];
[errorMessage saveWithTransaction:transaction];
2015-12-07 03:31:43 +01:00
}];
return;
}
// DEPRECATED - Remove after all clients have been upgraded.
NSData *encryptedData = messageEnvelope.hasContent ? messageEnvelope.content : messageEnvelope.legacyMessage;
if (!encryptedData) {
DDLogError(@"Skipping message envelope which had no encrypted data");
return;
}
2015-12-07 03:31:43 +01:00
NSData *plaintextData;
2015-12-07 03:31:43 +01:00
@try {
WhisperMessage *message = [[WhisperMessage alloc] initWithData:encryptedData];
2015-12-07 03:31:43 +01:00
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
preKeyStore:storageManager
signedPreKeyStore:storageManager
identityKeyStore:storageManager
recipientId:recipientId
deviceId:deviceId];
plaintextData = [[cipher decrypt:message] removePadding];
2015-12-07 03:31:43 +01:00
} @catch (NSException *exception) {
[self processException:exception envelope:messageEnvelope];
return;
}
if (messageEnvelope.hasContent) {
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
if (content.hasSyncMessage) {
[self handleIncomingEnvelope:messageEnvelope withSyncMessage:content.syncMessage];
} else if (content.dataMessage) {
[self handleIncomingEnvelope:messageEnvelope withDataMessage:content.dataMessage];
}
} else if (messageEnvelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded.
OWSSignalServiceProtosDataMessage *dataMessage =
[OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
[self handleIncomingEnvelope:messageEnvelope withDataMessage:dataMessage];
} else {
DDLogWarn(@"Ignoring content that has no dataMessage or syncMessage.");
}
2015-12-07 03:31:43 +01:00
}
}
- (void)handlePreKeyBundle:(OWSSignalServiceProtosEnvelope *)preKeyEnvelope
{
2015-12-07 03:31:43 +01:00
@synchronized(self) {
TSStorageManager *storageManager = [TSStorageManager sharedManager];
NSString *recipientId = preKeyEnvelope.source;
int deviceId = preKeyEnvelope.sourceDevice;
2015-12-07 03:31:43 +01:00
// DEPRECATED - Remove after all clients have been upgraded.
NSData *encryptedData = preKeyEnvelope.hasContent ? preKeyEnvelope.content : preKeyEnvelope.legacyMessage;
if (!encryptedData) {
DDLogError(@"Skipping message envelope which had no encrypted data");
return;
}
2015-12-07 03:31:43 +01:00
NSData *plaintextData;
2015-12-07 03:31:43 +01:00
@try {
PreKeyWhisperMessage *message = [[PreKeyWhisperMessage alloc] initWithData:encryptedData];
2015-12-07 03:31:43 +01:00
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
preKeyStore:storageManager
signedPreKeyStore:storageManager
identityKeyStore:storageManager
recipientId:recipientId
deviceId:deviceId];
plaintextData = [[cipher decrypt:message] removePadding];
2015-12-07 03:31:43 +01:00
} @catch (NSException *exception) {
[self processException:exception envelope:preKeyEnvelope];
return;
}
if (preKeyEnvelope.hasContent) {
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
if (content.hasSyncMessage) {
[self handleIncomingEnvelope:preKeyEnvelope withSyncMessage:content.syncMessage];
} else if (content.dataMessage) {
[self handleIncomingEnvelope:preKeyEnvelope withDataMessage:content.dataMessage];
}
} else if (preKeyEnvelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded.
OWSSignalServiceProtosDataMessage *dataMessage =
[OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
[self handleIncomingEnvelope:preKeyEnvelope withDataMessage:dataMessage];
} else {
DDLogWarn(@"Ignoring content that has no dataMessage or syncMessage.");
}
2015-12-07 03:31:43 +01:00
}
}
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)incomingEnvelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
if (dataMessage.hasGroup) {
2015-12-07 03:31:43 +01:00
__block BOOL ignoreMessage = NO;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
TSGroupModel *emptyModelToFillOutId =
[[TSGroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:dataMessage.group.id];
TSGroupThread *gThread = [TSGroupThread threadWithGroupModel:emptyModelToFillOutId transaction:transaction];
if (gThread == nil && dataMessage.group.type != OWSSignalServiceProtosGroupContextTypeUpdate) {
ignoreMessage = YES;
}
2015-12-07 03:31:43 +01:00
}];
if (ignoreMessage) {
// FIXME: https://github.com/WhisperSystems/Signal-iOS/issues/1340
DDLogDebug(@"%@ Received message from group that I left or don't know about, ignoring", self.tag);
2015-12-07 03:31:43 +01:00
return;
}
}
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
DDLogVerbose(@"%@ Received end session message", self.tag);
[self handleEndSessionMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
DDLogVerbose(@"%@ Received expiration timer update message", self.tag);
[self handleExpirationTimerUpdateMessageWithEnvelope:incomingEnvelope 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) {
DDLogVerbose(@"%@ Received media message attachment", self.tag);
[self handleReceivedMediaWithEnvelope:incomingEnvelope dataMessage:dataMessage];
2015-12-07 03:31:43 +01:00
} else {
DDLogVerbose(@"%@ Received data message.", self.tag);
[self handleReceivedTextMessageWithEnvelope:incomingEnvelope 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
if ([self isDataMessageGroupAvatarUpdate:dataMessage]) {
DDLogVerbose(@"%@ Data message had group avatar attachment", self.tag);
[self handleReceivedGroupAvatarUpdateWithEnvelope:incomingEnvelope dataMessage:dataMessage];
}
}
}
- (void)handleReceivedGroupAvatarUpdateWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
TSGroupThread *groupThread = [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ]
timestamp:envelope.timestamp
relay:envelope.relay
thread:groupThread
networkManager:self.networkManager];
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
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
[groupThread updateAvatarWithAttachmentStream:attachmentStream];
}
failure:^(NSError *_Nonnull error) {
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
{
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage];
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments
timestamp:envelope.timestamp
relay:envelope.relay
thread:thread
networkManager:self.networkManager];
if (!attachmentsProcessor.hasSupportedAttachments) {
DDLogWarn(@"%@ received unsupported media envelope", self.tag);
return;
}
TSIncomingMessage *createdMessage = [self handleReceivedEnvelope:envelope
withDataMessage:dataMessage
attachmentIds:attachmentsProcessor.supportedAttachmentIds];
[attachmentsProcessor fetchAttachmentsForMessage:createdMessage
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
DDLogDebug(
@"%@ successfully fetched attachment: %@ for message: %@", self.tag, attachmentStream, createdMessage);
}
failure:^(NSError *_Nonnull error) {
DDLogError(
@"%@ failed to fetch attachments for message: %@ with error: %@", self.tag, createdMessage, error);
}];
2015-12-07 03:31:43 +01:00
}
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)messageEnvelope
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
{
if (syncMessage.hasSent) {
DDLogInfo(@"%@ Received `sent` syncMessage, recording message transcript.", self.tag);
OWSIncomingSentMessageTranscript *transcript =
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:messageEnvelope.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];
if ([self isDataMessageGroupAvatarUpdate:syncMessage.sent.message]) {
[recordJob runWithAttachmentHandler:^(TSAttachmentStream *_Nonnull attachmentStream) {
TSGroupThread *groupThread =
[TSGroupThread getOrCreateThreadWithGroupIdData:syncMessage.sent.message.group.id];
[groupThread updateAvatarWithAttachmentStream:attachmentStream];
}];
} else {
[recordJob runWithAttachmentHandler:^(TSAttachmentStream *_Nonnull attachmentStream) {
DDLogDebug(@"%@ successfully fetched transcript attachment: %@", self.tag, attachmentStream);
}];
}
2016-09-01 16:28:35 +02:00
} else if (syncMessage.hasRequest) {
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
DDLogInfo(@"%@ Received request `Contacts` syncMessage.", self.tag);
OWSSyncContactsMessage *syncContactsMessage =
[[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager];
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
[self.messageSender sendTemporaryAttachmentData:[syncContactsMessage buildPlainTextAttachmentData]
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncContactsMessage
success:^{
DDLogInfo(@"%@ Successfully sent Contacts response syncMessage.", self.tag);
}
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
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send Contacts response syncMessage with error: %@", self.tag, error);
}];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
DDLogInfo(@"%@ Received request `groups` syncMessage.", self.tag);
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
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
[self.messageSender sendTemporaryAttachmentData:[syncGroupsMessage buildPlainTextAttachmentData]
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncGroupsMessage
success:^{
DDLogInfo(@"%@ Successfully sent Groups response syncMessage.", self.tag);
}
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
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send Groups response syncMessage with error: %@", self.tag, error);
}];
} else {
DDLogWarn(@"%@ ignoring unsupported sync request message", self.tag);
}
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];
2016-09-01 16:28:35 +02:00
[readReceiptsProcessor process];
} else {
DDLogWarn(@"%@ Ignoring unsupported sync message.", self.tag);
}
}
- (void)handleEndSessionMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)endSessionEnvelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
2015-12-07 03:31:43 +01:00
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSContactThread *thread =
[TSContactThread getOrCreateThreadWithContactId:endSessionEnvelope.source transaction:transaction];
uint64_t timeStamp = endSessionEnvelope.timestamp;
if (thread) { // TODO thread should always be nonnull.
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
inThread:thread
messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction];
}
2015-12-07 03:31:43 +01:00
}];
[[TSStorageManager sharedManager] deleteAllSessionsForContact:endSessionEnvelope.source];
2015-12-07 03:31:43 +01:00
}
- (void)handleExpirationTimerUpdateMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage];
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];
}
[disappearingMessagesConfiguration save];
NSString *name = [self.contactsManager displayNameForPhoneIdentifier:envelope.source];
OWSDisappearingConfigurationUpdateInfoMessage *message =
[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:envelope.timestamp
thread:thread
configuration:disappearingMessagesConfiguration
createdByRemoteName:name];
[message save];
2015-12-07 03:31:43 +01:00
}
- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)textMessageEnvelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
[self handleReceivedEnvelope:textMessageEnvelope withDataMessage:dataMessage attachmentIds:@[]];
2015-12-07 03:31:43 +01:00
}
- (TSIncomingMessage *)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds
{
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;
2015-12-07 03:31:43 +01:00
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
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];
2015-12-07 03:31:43 +01:00
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: {
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
inThread:gThread
authorId:envelope.source
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);
}
2015-12-07 03:31:43 +01:00
}
thread = gThread;
} else {
TSContactThread *cThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source
2015-12-07 03:31:43 +01:00
transaction:transaction
relay:envelope.relay];
2015-12-07 03:31:43 +01:00
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
2015-12-07 03:31:43 +01:00
inThread:cThread
authorId:[cThread contactIdentifier]
2015-12-07 03:31:43 +01:00
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
2015-12-07 03:31:43 +01:00
thread = cThread;
}
if (thread && incomingMessage) {
[incomingMessage saveWithTransaction:transaction];
// Android allows attachments to be sent with body.
if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) {
// We want the text to be displayed under the attachment
uint64_t textMessageTimestamp = timestamp + 1;
TSIncomingMessage *textMessage;
2015-12-07 03:31:43 +01:00
if ([thread isGroupThread]) {
TSGroupThread *gThread = (TSGroupThread *)thread;
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:gThread
authorId:envelope.source
messageBody:body];
2015-12-07 03:31:43 +01:00
} else {
TSContactThread *cThread = (TSContactThread *)thread;
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:cThread
authorId:[cThread contactIdentifier]
messageBody:body];
2015-12-07 03:31:43 +01:00
}
textMessage.expiresInSeconds = dataMessage.expireTimer;
[textMessage saveWithTransaction:transaction];
2015-12-07 03:31:43 +01:00
}
}
}];
2015-12-07 03:31:43 +01:00
if (incomingMessage && thread) {
// 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];
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
[self.disappearingMessagesJob becomeConsistentWithConfigurationForMessage:incomingMessage
contactsManager:self.contactsManager];
// Update thread preview in inbox
[thread touch];
// TODO Delay notification by 100ms?
// It's pretty annoying when you're phone keeps buzzing while you're having a conversation on Desktop.
NSString *name = [thread name];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForIncomingMessage:incomingMessage
from:name
inThread:thread];
}
return incomingMessage;
2015-12-07 03:31:43 +01:00
}
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
{
DDLogError(@"%@ Got exception: %@ of type: %@", self.tag, exception.description, exception.name);
2015-12-07 03:31:43 +01:00
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage;
if ([exception.name isEqualToString:NoSessionException]) {
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
2015-12-07 03:31:43 +01:00
} else if ([exception.name isEqualToString:InvalidKeyException]) {
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
2015-12-07 03:31:43 +01:00
} else if ([exception.name isEqualToString:InvalidKeyIdException]) {
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
2015-12-07 03:31:43 +01:00
} else if ([exception.name isEqualToString:DuplicateMessageException]) {
// Duplicate messages are dismissed
return;
} else if ([exception.name isEqualToString:InvalidVersionException]) {
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
2015-12-07 03:31:43 +01:00
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
errorMessage =
[TSInvalidIdentityKeyReceivingErrorMessage untrustedKeyWithEnvelope:envelope withTransaction:transaction];
2015-12-07 03:31:43 +01:00
} else {
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
2015-12-07 03:31:43 +01:00
}
[errorMessage saveWithTransaction: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
#pragma mark - helpers
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
- (BOOL)isDataMessageGroupAvatarUpdate:(OWSSignalServiceProtosDataMessage *)dataMessage
{
return dataMessage.hasGroup
&& dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
&& dataMessage.group.hasAvatar;
2015-12-07 03:31:43 +01:00
}
- (TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
if (dataMessage.hasGroup) {
return [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
} else {
return [TSContactThread getOrCreateThreadWithContactId:envelope.source];
}
}
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) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
numberOfItems =
numberOfItems - [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
}];
return numberOfItems;
}
- (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