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"
|
2016-08-26 01:01:35 +02:00
|
|
|
#import "ContactsManagerProtocol.h"
|
2016-09-23 22:55:56 +02:00
|
|
|
#import "ContactsUpdater.h"
|
2016-08-26 01:01:35 +02:00
|
|
|
#import "MimeTypeUtil.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "NSData+messagePadding.h"
|
2016-10-05 17:42:44 +02:00
|
|
|
#import "NSDate+millisecondTimeStamp.h"
|
|
|
|
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
|
|
|
|
#import "OWSDisappearingMessagesConfiguration.h"
|
|
|
|
#import "OWSDisappearingMessagesJob.h"
|
2016-08-23 22:38:05 +02:00
|
|
|
#import "OWSIncomingSentMessageTranscript.h"
|
2016-09-01 16:28:35 +02:00
|
|
|
#import "OWSReadReceiptsProcessor.h"
|
2016-10-05 17:42:44 +02:00
|
|
|
#import "OWSRecordTranscriptJob.h"
|
2016-08-26 01:01:35 +02:00
|
|
|
#import "OWSSyncContactsMessage.h"
|
2016-08-27 00:07:54 +02:00
|
|
|
#import "OWSSyncGroupsMessage.h"
|
2016-04-08 09:38:34 +02:00
|
|
|
#import "TSAccountManager.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "TSAttachmentStream.h"
|
|
|
|
#import "TSCall.h"
|
2016-07-28 01:58:49 +02:00
|
|
|
#import "TSContactThread.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "TSDatabaseView.h"
|
2016-07-28 01:58:49 +02:00
|
|
|
#import "TSGroupModel.h"
|
|
|
|
#import "TSGroupThread.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "TSInfoMessage.h"
|
|
|
|
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
|
|
|
#import "TSMessagesManager+attachments.h"
|
2016-09-23 22:55:56 +02:00
|
|
|
#import "TSNetworkManager.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "TSStorageHeaders.h"
|
|
|
|
#import "TextSecureKitEnv.h"
|
2016-07-28 04:29:27 +02:00
|
|
|
#import <AxolotlKit/AxolotlExceptions.h>
|
|
|
|
#import <AxolotlKit/SessionCipher.h>
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
2016-10-01 20:42:39 +02:00
|
|
|
@interface TSMessagesManager ()
|
|
|
|
|
|
|
|
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
|
2016-10-05 17:42:44 +02:00
|
|
|
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
2016-10-01 20:42:39 +02:00
|
|
|
|
|
|
|
@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, ^{
|
|
|
|
sharedMyManager = [[self alloc] init];
|
|
|
|
});
|
|
|
|
return sharedMyManager;
|
|
|
|
}
|
|
|
|
|
2016-09-23 22:55:56 +02:00
|
|
|
- (instancetype)init
|
|
|
|
{
|
|
|
|
return [self initWithNetworkManager:[TSNetworkManager sharedManager]
|
2016-10-05 17:42:44 +02:00
|
|
|
storageManager:[TSStorageManager sharedManager]
|
2016-10-01 20:42:39 +02:00
|
|
|
contactsManager:[TextSecureKitEnv sharedEnv].contactsManager
|
2016-09-23 22:55:56 +02:00
|
|
|
contactsUpdater:[ContactsUpdater sharedUpdater]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
|
2016-10-05 17:42:44 +02:00
|
|
|
storageManager:(TSStorageManager *)storageManager
|
2016-10-01 20:42:39 +02:00
|
|
|
contactsManager:(id<ContactsManagerProtocol>)contactsManager
|
2016-09-23 22:55:56 +02:00
|
|
|
contactsUpdater:(ContactsUpdater *)contactsUpdater
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
self = [super init];
|
|
|
|
|
2016-09-23 22:55:56 +02:00
|
|
|
if (!self) {
|
|
|
|
return self;
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
_storageManager = storageManager;
|
2016-09-23 22:55:56 +02:00
|
|
|
_networkManager = networkManager;
|
2016-10-01 20:42:39 +02:00
|
|
|
_contactsManager = contactsManager;
|
2016-09-23 22:55:56 +02:00
|
|
|
_contactsUpdater = contactsUpdater;
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
_dbConnection = storageManager.newDatabaseConnection;
|
|
|
|
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
@try {
|
2016-08-22 22:09:58 +02:00
|
|
|
switch (envelope.type) {
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeCiphertext:
|
|
|
|
[self handleSecureMessage:envelope];
|
2015-12-07 03:31:43 +01:00
|
|
|
break;
|
2016-08-22 22:09:58 +02:00
|
|
|
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.
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
|
2015-12-07 03:31:43 +01:00
|
|
|
DDLogWarn(@"Received Key Exchange Message, not supported");
|
|
|
|
break;
|
2016-08-22 22:09:58 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)handleDeliveryReceipt:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
2016-08-22 22:09:58 +02:00
|
|
|
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
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
[outgoingMessage saveWithTransaction:transaction];
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)handleSecureMessage:(OWSSignalServiceProtosEnvelope *)messageEnvelope
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
@synchronized(self) {
|
|
|
|
TSStorageManager *storageManager = [TSStorageManager sharedManager];
|
2016-08-22 22:09:58 +02:00
|
|
|
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) {
|
2016-08-22 22:09:58 +02:00
|
|
|
TSErrorMessage *errorMessage =
|
|
|
|
[TSErrorMessage missingSessionWithEnvelope:messageEnvelope withTransaction:transaction];
|
|
|
|
[errorMessage saveWithTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
}];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
// 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
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
NSData *plaintextData;
|
2015-12-07 03:31:43 +01:00
|
|
|
@try {
|
2016-08-22 22:09:58 +02:00
|
|
|
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];
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
plaintextData = [[cipher decrypt:message] removePadding];
|
2015-12-07 03:31:43 +01:00
|
|
|
} @catch (NSException *exception) {
|
2016-08-22 22:09:58 +02:00
|
|
|
[self processException:exception envelope:messageEnvelope];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
if (messageEnvelope.hasContent) {
|
2016-08-22 22:09:58 +02:00
|
|
|
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
|
2016-08-23 22:38:05 +02:00
|
|
|
if (content.hasSyncMessage) {
|
|
|
|
[self handleIncomingEnvelope:messageEnvelope withSyncMessage:content.syncMessage];
|
|
|
|
} else if (content.dataMessage) {
|
|
|
|
[self handleIncomingEnvelope:messageEnvelope withDataMessage:content.dataMessage];
|
|
|
|
}
|
2016-08-22 22:09:58 +02:00
|
|
|
} else if (messageEnvelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded.
|
2016-08-23 22:38:05 +02:00
|
|
|
OWSSignalServiceProtosDataMessage *dataMessage =
|
|
|
|
[OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
|
|
|
|
[self handleIncomingEnvelope:messageEnvelope withDataMessage:dataMessage];
|
|
|
|
} else {
|
|
|
|
DDLogWarn(@"Ignoring content that has no dataMessage or syncMessage.");
|
2016-08-22 22:09:58 +02:00
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)handlePreKeyBundle:(OWSSignalServiceProtosEnvelope *)preKeyEnvelope
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
@synchronized(self) {
|
|
|
|
TSStorageManager *storageManager = [TSStorageManager sharedManager];
|
2016-08-22 22:09:58 +02:00
|
|
|
NSString *recipientId = preKeyEnvelope.source;
|
|
|
|
int deviceId = preKeyEnvelope.sourceDevice;
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-08-22 22:09:58 +02: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
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
NSData *plaintextData;
|
2015-12-07 03:31:43 +01:00
|
|
|
@try {
|
2016-08-22 22:09:58 +02:00
|
|
|
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];
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
plaintextData = [[cipher decrypt:message] removePadding];
|
2015-12-07 03:31:43 +01:00
|
|
|
} @catch (NSException *exception) {
|
2016-08-22 22:09:58 +02:00
|
|
|
[self processException:exception envelope:preKeyEnvelope];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (preKeyEnvelope.hasContent) {
|
|
|
|
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
|
2016-08-23 22:38:05 +02:00
|
|
|
if (content.hasSyncMessage) {
|
|
|
|
[self handleIncomingEnvelope:preKeyEnvelope withSyncMessage:content.syncMessage];
|
|
|
|
} else if (content.dataMessage) {
|
|
|
|
[self handleIncomingEnvelope:preKeyEnvelope withDataMessage:content.dataMessage];
|
2016-08-22 22:09:58 +02:00
|
|
|
}
|
2016-08-23 22:38:05 +02:00
|
|
|
} 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.");
|
2016-08-22 22:09:58 +02:00
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)incomingEnvelope
|
|
|
|
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
2016-07-31 02:40:14 +02:00
|
|
|
{
|
2016-08-22 22:09:58 +02:00
|
|
|
if (dataMessage.hasGroup) {
|
2015-12-07 03:31:43 +01:00
|
|
|
__block BOOL ignoreMessage = NO;
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
2016-07-31 02:40:14 +02:00
|
|
|
TSGroupModel *emptyModelToFillOutId =
|
2016-08-22 22:09:58 +02:00
|
|
|
[[TSGroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:dataMessage.group.id];
|
2016-07-31 02:40:14 +02:00
|
|
|
TSGroupThread *gThread = [TSGroupThread threadWithGroupModel:emptyModelToFillOutId transaction:transaction];
|
2016-08-22 22:09:58 +02:00
|
|
|
if (gThread == nil && dataMessage.group.type != OWSSignalServiceProtosGroupContextTypeUpdate) {
|
2016-07-31 02:40:14 +02:00
|
|
|
ignoreMessage = YES;
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}];
|
|
|
|
if (ignoreMessage) {
|
2016-10-05 17:42:44 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2016-08-22 22:09:58 +02:00
|
|
|
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogVerbose(@"%@ Received end session message", self.tag);
|
2016-08-22 22:09:58 +02:00
|
|
|
[self handleEndSessionMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
2016-10-05 17:42:44 +02:00
|
|
|
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
|
|
|
|
DDLogVerbose(@"%@ Received expiration timer update message", self.tag);
|
|
|
|
[self handleExpirationTimerUpdateMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
2016-08-22 22:09:58 +02:00
|
|
|
} else if (dataMessage.attachments.count > 0
|
|
|
|
|| (dataMessage.hasGroup && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
|
2016-09-21 00:57:11 +02:00
|
|
|
&& dataMessage.group.hasAvatar)) {
|
2016-08-22 22:09:58 +02:00
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogVerbose(@"%@ Received push media message (attachment) or group with an avatar", self.tag);
|
2016-08-22 22:09:58 +02:00
|
|
|
[self handleReceivedMediaWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
2015-12-07 03:31:43 +01:00
|
|
|
} else {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogVerbose(@"%@ Received data message.", self.tag);
|
2016-08-22 22:09:58 +02:00
|
|
|
[self handleReceivedTextMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)messageEnvelope
|
|
|
|
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
|
|
|
|
{
|
|
|
|
if (syncMessage.hasSent) {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogInfo(@"%@ Received `sent` syncMessage, recording message transcript.", self.tag);
|
2016-08-23 22:38:05 +02:00
|
|
|
OWSIncomingSentMessageTranscript *transcript =
|
|
|
|
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:messageEnvelope.relay];
|
2016-10-05 17:42:44 +02:00
|
|
|
[[[OWSRecordTranscriptJob alloc] initWithMessagesManager:self incomingSentMessageTranscript:transcript] run];
|
2016-09-01 16:28:35 +02:00
|
|
|
} else if (syncMessage.hasRequest) {
|
2016-08-26 01:01:35 +02:00
|
|
|
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogInfo(@"%@ Received request `Contacts` syncMessage.", self.tag);
|
2016-08-26 01:01:35 +02:00
|
|
|
|
|
|
|
OWSSyncContactsMessage *syncContactsMessage =
|
2016-10-01 20:42:39 +02:00
|
|
|
[[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager];
|
2016-08-26 01:01:35 +02:00
|
|
|
|
2016-09-08 15:41:47 +02:00
|
|
|
[self sendTemporaryAttachment:[syncContactsMessage buildPlainTextAttachmentData]
|
2016-08-26 01:01:35 +02:00
|
|
|
contentType:OWSMimeTypeApplicationOctetStream
|
|
|
|
inMessage:syncContactsMessage
|
|
|
|
thread:nil
|
|
|
|
success:^{
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogInfo(@"%@ Successfully sent Contacts response syncMessage.", self.tag);
|
2016-08-26 01:01:35 +02:00
|
|
|
}
|
|
|
|
failure:^{
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogError(@"%@ Failed to send Contacts response syncMessage.", self.tag);
|
2016-08-26 01:01:35 +02:00
|
|
|
}];
|
|
|
|
|
|
|
|
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogInfo(@"%@ Received request `groups` syncMessage.", self.tag);
|
2016-08-27 00:07:54 +02:00
|
|
|
|
|
|
|
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
|
|
|
|
|
2016-09-08 15:41:47 +02:00
|
|
|
[self sendTemporaryAttachment:[syncGroupsMessage buildPlainTextAttachmentData]
|
2016-08-27 00:07:54 +02:00
|
|
|
contentType:OWSMimeTypeApplicationOctetStream
|
|
|
|
inMessage:syncGroupsMessage
|
|
|
|
thread:nil
|
|
|
|
success:^{
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogInfo(@"%@ Successfully sent Groups response syncMessage.", self.tag);
|
2016-08-27 00:07:54 +02:00
|
|
|
}
|
|
|
|
failure:^{
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogError(@"%@ Failed to send Groups response syncMessage.", self.tag);
|
2016-08-27 00:07:54 +02:00
|
|
|
}];
|
2016-08-26 01:01:35 +02:00
|
|
|
}
|
2016-09-01 16:28:35 +02:00
|
|
|
} else if (syncMessage.read.count > 0) {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogInfo(@"%@ Received %ld read receipt(s)", self.tag, (u_long)syncMessage.read.count);
|
2016-09-01 16:28:35 +02:00
|
|
|
|
|
|
|
OWSReadReceiptsProcessor *readReceiptsProcessor =
|
2016-10-05 17:42:44 +02:00
|
|
|
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read
|
|
|
|
storageManager:self.storageManager];
|
2016-09-01 16:28:35 +02:00
|
|
|
[readReceiptsProcessor process];
|
2016-08-23 22:38:05 +02:00
|
|
|
} else {
|
2016-10-05 17:42:44 +02:00
|
|
|
DDLogWarn(@"%@ Ignoring unsupported sync message.", self.tag);
|
2016-08-23 22:38:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)handleEndSessionMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)endSessionEnvelope
|
|
|
|
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
2016-08-22 22:09:58 +02:00
|
|
|
TSContactThread *thread =
|
|
|
|
[TSContactThread getOrCreateThreadWithContactId:endSessionEnvelope.source transaction:transaction];
|
|
|
|
uint64_t timeStamp = endSessionEnvelope.timestamp;
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
if (thread) { // TODO thread should always be nonnull.
|
2016-08-22 22:09:58 +02:00
|
|
|
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
|
|
|
|
inThread:thread
|
|
|
|
messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction];
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}];
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
[[TSStorageManager sharedManager] deleteAllSessionsForContact:endSessionEnvelope.source];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
- (void)handleExpirationTimerUpdateMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
2016-07-29 21:33:25 +02:00
|
|
|
{
|
2016-10-05 17:42:44 +02:00
|
|
|
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 nameStringForPhoneIdentifier: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
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)textMessageEnvelope
|
|
|
|
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
2016-07-29 21:33:25 +02:00
|
|
|
{
|
2016-10-05 17:42:44 +02:00
|
|
|
[self handleReceivedEnvelope:textMessageEnvelope withDataMessage:dataMessage attachmentIds:@[]];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
- (TSIncomingMessage *)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
|
|
|
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
2016-07-29 21:33:25 +02:00
|
|
|
{
|
2016-10-05 17:42:44 +02:00
|
|
|
uint64_t timestamp = envelope.timestamp;
|
2016-08-22 22:09:58 +02:00
|
|
|
NSString *body = dataMessage.body;
|
|
|
|
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
__block TSIncomingMessage *_Nullable incomingMessage;
|
2016-09-13 16:15:01 +02:00
|
|
|
__block TSThread *thread;
|
2016-08-23 22:38:05 +02:00
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
if (groupId) {
|
2016-08-22 22:09:58 +02:00
|
|
|
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];
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate) {
|
2016-07-29 21:33:25 +02:00
|
|
|
if ([attachmentIds count] == 1) {
|
|
|
|
NSString *avatarId = attachmentIds[0];
|
2015-12-07 03:31:43 +01:00
|
|
|
TSAttachment *avatar = [TSAttachment fetchObjectWithUniqueID:avatarId];
|
|
|
|
if ([avatar isKindOfClass:[TSAttachmentStream class]]) {
|
|
|
|
TSAttachmentStream *stream = (TSAttachmentStream *)avatar;
|
|
|
|
if ([stream isImage]) {
|
2016-07-31 02:40:14 +02:00
|
|
|
model.groupImage = [stream image];
|
|
|
|
// No need to keep the attachment around after assigning the image.
|
|
|
|
[stream removeWithTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString *updateGroupInfo = [gThread.groupModel getInfoStringAboutUpdateTo:model];
|
|
|
|
gThread.groupModel = model;
|
|
|
|
[gThread saveWithTransaction:transaction];
|
2016-10-05 17:42:44 +02:00
|
|
|
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
|
2015-12-07 03:31:43 +01:00
|
|
|
inThread:gThread
|
|
|
|
messageType:TSInfoMessageTypeGroupUpdate
|
|
|
|
customMessage:updateGroupInfo] saveWithTransaction:transaction];
|
2016-08-22 22:09:58 +02:00
|
|
|
} else if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeQuit) {
|
2016-10-01 20:42:39 +02:00
|
|
|
NSString *nameString = [self.contactsManager nameStringForPhoneIdentifier:envelope.source];
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
NSString *updateGroupInfo =
|
|
|
|
[NSString stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_LEFT", @""), nameString];
|
|
|
|
NSMutableArray *newGroupMembers = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds];
|
2016-08-22 22:09:58 +02:00
|
|
|
[newGroupMembers removeObject:envelope.source];
|
2015-12-07 03:31:43 +01:00
|
|
|
gThread.groupModel.groupMemberIds = newGroupMembers;
|
|
|
|
|
|
|
|
[gThread saveWithTransaction:transaction];
|
2016-10-05 17:42:44 +02:00
|
|
|
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
|
2015-12-07 03:31:43 +01:00
|
|
|
inThread:gThread
|
|
|
|
messageType:TSInfoMessageTypeGroupUpdate
|
|
|
|
customMessage:updateGroupInfo] saveWithTransaction:transaction];
|
|
|
|
} else {
|
2016-10-05 17:42:44 +02:00
|
|
|
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
|
2015-12-07 03:31:43 +01:00
|
|
|
inThread:gThread
|
2016-08-22 22:09:58 +02:00
|
|
|
authorId:envelope.source
|
2015-12-07 03:31:43 +01:00
|
|
|
messageBody:body
|
2016-10-05 17:42:44 +02:00
|
|
|
attachmentIds:attachmentIds
|
|
|
|
expiresInSeconds:dataMessage.expireTimer];
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
[incomingMessage saveWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
|
|
|
|
thread = gThread;
|
|
|
|
} else {
|
2016-08-22 22:09:58 +02:00
|
|
|
TSContactThread *cThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source
|
2015-12-07 03:31:43 +01:00
|
|
|
transaction:transaction
|
2016-08-25 16:18:10 +02:00
|
|
|
relay:envelope.relay];
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
|
2015-12-07 03:31:43 +01:00
|
|
|
inThread:cThread
|
2016-10-07 01:42:52 +02:00
|
|
|
authorId:[cThread contactIdentifier]
|
2015-12-07 03:31:43 +01:00
|
|
|
messageBody:body
|
2016-10-05 17:42:44 +02:00
|
|
|
attachmentIds:attachmentIds
|
|
|
|
expiresInSeconds:dataMessage.expireTimer];
|
2015-12-07 03:31:43 +01:00
|
|
|
thread = cThread;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thread && incomingMessage) {
|
2016-10-05 17:42:44 +02:00
|
|
|
[incomingMessage saveWithTransaction:transaction];
|
|
|
|
|
2016-07-29 21:33:25 +02:00
|
|
|
// Android allows attachments to be sent with body.
|
|
|
|
if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) {
|
2016-10-05 17:42:44 +02:00
|
|
|
// 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]) {
|
2016-07-29 21:33:25 +02:00
|
|
|
TSGroupThread *gThread = (TSGroupThread *)thread;
|
2016-10-05 17:42:44 +02:00
|
|
|
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
|
|
|
|
inThread:gThread
|
|
|
|
authorId:envelope.source
|
|
|
|
messageBody:body];
|
2015-12-07 03:31:43 +01:00
|
|
|
} else {
|
2016-07-29 21:33:25 +02:00
|
|
|
TSContactThread *cThread = (TSContactThread *)thread;
|
2016-10-05 17:42:44 +02:00
|
|
|
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
|
|
|
|
inThread:cThread
|
2016-10-07 01:42:52 +02:00
|
|
|
authorId:[cThread contactIdentifier]
|
2016-10-05 17:42:44 +02:00
|
|
|
messageBody:body];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
2016-10-05 17:42:44 +02:00
|
|
|
textMessage.expiresInSeconds = dataMessage.expireTimer;
|
|
|
|
[textMessage saveWithTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
}
|
2016-09-13 16:15:01 +02:00
|
|
|
}];
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-09-13 16:15:01 +02:00
|
|
|
if (incomingMessage && thread) {
|
2016-10-05 17:42:44 +02:00
|
|
|
// In case we already have a read receipt for this new message (happens sometimes).
|
2016-09-13 16:15:01 +02:00
|
|
|
OWSReadReceiptsProcessor *readReceiptsProcessor =
|
2016-10-05 17:42:44 +02:00
|
|
|
[[OWSReadReceiptsProcessor alloc] initWithIncomingMessage:incomingMessage
|
|
|
|
storageManager:self.storageManager];
|
2016-09-13 16:15:01 +02:00
|
|
|
[readReceiptsProcessor process];
|
2016-09-08 20:21:10 +02:00
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
[self becomeConsistentWithDisappearingConfigurationForMessage:incomingMessage];
|
|
|
|
|
|
|
|
// Update thread preview in inbox
|
|
|
|
[thread touch];
|
|
|
|
|
2016-09-13 16:15:01 +02:00
|
|
|
// 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];
|
|
|
|
}
|
2016-08-23 22:38:05 +02:00
|
|
|
|
|
|
|
return incomingMessage;
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSMessage *)message
|
|
|
|
{
|
|
|
|
// Become eventually consistent in the case that the remote changed their settings at the same time.
|
|
|
|
// Also in case remote doesn't support expiring messages
|
|
|
|
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
|
|
|
|
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId];
|
|
|
|
|
|
|
|
BOOL changed = NO;
|
|
|
|
if (message.expiresInSeconds == 0) {
|
|
|
|
if (disappearingMessagesConfiguration.isEnabled) {
|
|
|
|
changed = YES;
|
|
|
|
DDLogWarn(@"%@ Received remote message which had no expiration set, disabling our expiration to become "
|
|
|
|
@"consistent.",
|
|
|
|
self.tag);
|
|
|
|
disappearingMessagesConfiguration.enabled = NO;
|
|
|
|
[disappearingMessagesConfiguration save];
|
|
|
|
}
|
|
|
|
} else if (message.expiresInSeconds != disappearingMessagesConfiguration.durationSeconds) {
|
|
|
|
changed = YES;
|
|
|
|
DDLogInfo(
|
|
|
|
@"%@ Received remote message with different expiration set, updating our expiration to become consistent.",
|
|
|
|
self.tag);
|
|
|
|
disappearingMessagesConfiguration.enabled = YES;
|
|
|
|
disappearingMessagesConfiguration.durationSeconds = message.expiresInSeconds;
|
|
|
|
[disappearingMessagesConfiguration save];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!changed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([message isKindOfClass:[TSIncomingMessage class]]) {
|
|
|
|
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message;
|
|
|
|
NSString *contactName = [self.contactsManager nameStringForPhoneIdentifier:incomingMessage.authorId];
|
|
|
|
|
|
|
|
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp
|
|
|
|
thread:message.thread
|
|
|
|
configuration:disappearingMessagesConfiguration
|
|
|
|
createdByRemoteName:contactName] save];
|
|
|
|
} else {
|
|
|
|
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp
|
|
|
|
thread:message.thread
|
|
|
|
configuration:disappearingMessagesConfiguration]
|
|
|
|
save];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
{
|
2016-10-05 17:42:44 +02:00
|
|
|
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]) {
|
2016-08-22 22:09:58 +02:00
|
|
|
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
} else if ([exception.name isEqualToString:InvalidKeyException]) {
|
2016-08-22 22:09:58 +02:00
|
|
|
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
} else if ([exception.name isEqualToString:InvalidKeyIdException]) {
|
2016-08-22 22:09:58 +02:00
|
|
|
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]) {
|
2016-08-22 22:09:58 +02:00
|
|
|
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
|
|
|
|
errorMessage =
|
2016-08-22 22:09:58 +02:00
|
|
|
[TSInvalidIdentityKeyReceivingErrorMessage untrustedKeyWithEnvelope:envelope withTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
} else {
|
2016-08-22 22:09:58 +02:00
|
|
|
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[errorMessage saveWithTransaction:transaction];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)processException:(NSException *)exception
|
|
|
|
outgoingMessage:(TSOutgoingMessage *)message
|
2016-10-05 17:42:44 +02:00
|
|
|
inThread:(TSThread *)thread
|
|
|
|
{
|
|
|
|
DDLogWarn(@"%@ Got exception: %@", self.tag, exception);
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
2016-09-20 03:57:20 +02:00
|
|
|
TSErrorMessage *errorMessage;
|
|
|
|
|
|
|
|
if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
|
|
|
|
errorMessage = [TSInvalidIdentityKeySendingErrorMessage
|
|
|
|
untrustedKeyWithOutgoingMessage:message
|
|
|
|
inThread:thread
|
|
|
|
forRecipient:exception.userInfo[TSInvalidRecipientKey]
|
|
|
|
preKeyBundle:exception.userInfo[TSInvalidPreKeyBundleKey]
|
|
|
|
withTransaction:transaction];
|
|
|
|
message.messageState = TSOutgoingMessageStateUnsent;
|
|
|
|
[message saveWithTransaction:transaction];
|
|
|
|
} else if (message.groupMetaMessage == TSGroupMessageNone) {
|
|
|
|
// Only update this with exception if it is not a group message as group
|
|
|
|
// messages may except for one group
|
|
|
|
// send but not another and the UI doesn't know how to handle that
|
|
|
|
[message setMessageState:TSOutgoingMessageStateUnsent];
|
|
|
|
[message saveWithTransaction:transaction];
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-09-20 03:57:20 +02:00
|
|
|
[errorMessage saveWithTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02: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;
|
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
#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
|
2016-10-05 17:42:44 +02:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|