2017-01-24 21:47:41 +01:00
//
// Copyright ( c ) 2017 Open Whisper Systems . All rights reserved .
//
2015-12-07 03:31:43 +01:00
# 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"
2016-12-18 22:08:26 +01:00
# import "NotificationsProtocol.h"
2016-10-14 23:00:29 +02:00
# import "OWSAttachmentsProcessor.h"
2017-04-03 20:42:04 +02:00
# import "OWSBlockingManager.h"
2016-12-18 22:08:26 +01:00
# import "OWSCallMessageHandler.h"
2016-10-05 17:42:44 +02:00
# import "OWSDisappearingConfigurationUpdateInfoMessage.h"
# import "OWSDisappearingMessagesConfiguration.h"
# import "OWSDisappearingMessagesJob.h"
2017-01-24 21:47:41 +01:00
# import "OWSError.h"
2017-02-16 00:32:27 +01:00
# import "OWSIncomingMessageFinder.h"
2016-08-23 22:38:05 +02:00
# import "OWSIncomingSentMessageTranscript.h"
2016-10-14 23:00:29 +02:00
# import "OWSMessageSender.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"
2017-05-05 23:22:56 +02:00
# import "OWSSyncGroupsRequestMessage.h"
2017-08-02 19:12:26 +02:00
# import "ProfileManagerProtocol.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"
2016-09-23 22:55:56 +02:00
# import "TSNetworkManager.h"
2017-02-10 01:35:10 +01:00
# import "TSPreKeyManager.h"
2015-12-07 03:31:43 +01:00
# import "TSStorageHeaders.h"
# import "TextSecureKitEnv.h"
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 ( )
2016-12-18 22:08:26 +01:00
@ property ( nonatomic , readonly ) id < OWSCallMessageHandler > callMessageHandler ;
2016-10-01 20:42:39 +02:00
@ property ( nonatomic , readonly ) id < ContactsManagerProtocol > contactsManager ;
2016-10-05 17:42:44 +02:00
@ property ( nonatomic , readonly ) TSStorageManager * storageManager ;
2016-10-14 23:00:29 +02:00
@ property ( nonatomic , readonly ) OWSMessageSender * messageSender ;
2017-02-16 00:32:27 +01:00
@ property ( nonatomic , readonly ) OWSIncomingMessageFinder * incomingMessageFinder ;
2017-04-03 20:42:04 +02:00
@ property ( nonatomic , readonly ) OWSBlockingManager * blockingManager ;
2017-06-22 03:04:16 +02:00
@ property ( nonatomic , readonly ) OWSIdentityManager * identityManager ;
2016-10-01 20:42:39 +02:00
@ end
2017-05-09 20:38:49 +02:00
# pragma mark -
2015-12-07 03:31:43 +01:00
@ implementation TSMessagesManager
+ ( instancetype ) sharedManager {
static TSMessagesManager * sharedMyManager = nil ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
2016-10-14 23:00:29 +02:00
sharedMyManager = [ [ self alloc ] initDefault ] ;
2015-12-07 03:31:43 +01:00
} ) ;
return sharedMyManager ;
}
2016-10-14 23:00:29 +02:00
- ( instancetype ) initDefault
2016-09-23 22:55:56 +02:00
{
2016-10-14 23:00:29 +02:00
TSNetworkManager * networkManager = [ TSNetworkManager sharedManager ] ;
TSStorageManager * storageManager = [ TSStorageManager sharedManager ] ;
id < ContactsManagerProtocol > contactsManager = [ TextSecureKitEnv sharedEnv ] . contactsManager ;
2016-12-18 22:08:26 +01:00
id < OWSCallMessageHandler > callMessageHandler = [ TextSecureKitEnv sharedEnv ] . callMessageHandler ;
2016-10-14 23:00:29 +02:00
ContactsUpdater * contactsUpdater = [ ContactsUpdater sharedUpdater ] ;
2017-06-22 03:04:16 +02:00
OWSIdentityManager * identityManager = [ OWSIdentityManager sharedManager ] ;
2017-03-30 18:37:22 +02:00
OWSMessageSender * messageSender = [ TextSecureKitEnv sharedEnv ] . messageSender ;
2017-06-22 03:04:16 +02:00
2016-10-14 23:00:29 +02:00
return [ self initWithNetworkManager : networkManager
storageManager : storageManager
2016-12-18 22:08:26 +01:00
callMessageHandler : callMessageHandler
2016-10-14 23:00:29 +02:00
contactsManager : contactsManager
contactsUpdater : contactsUpdater
2017-06-22 03:04:16 +02:00
identityManager : identityManager
2016-10-14 23:00:29 +02:00
messageSender : messageSender ] ;
2016-09-23 22:55:56 +02:00
}
- ( instancetype ) initWithNetworkManager : ( TSNetworkManager * ) networkManager
2016-10-05 17:42:44 +02:00
storageManager : ( TSStorageManager * ) storageManager
2016-12-18 22:08:26 +01:00
callMessageHandler : ( id < OWSCallMessageHandler > ) callMessageHandler
2016-10-01 20:42:39 +02:00
contactsManager : ( id < ContactsManagerProtocol > ) contactsManager
2016-09-23 22:55:56 +02:00
contactsUpdater : ( ContactsUpdater * ) contactsUpdater
2017-06-22 03:04:16 +02:00
identityManager : ( OWSIdentityManager * ) identityManager
2016-10-14 23:00:29 +02:00
messageSender : ( OWSMessageSender * ) messageSender
2016-09-23 22:55:56 +02:00
{
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-12-18 22:08:26 +01:00
_callMessageHandler = callMessageHandler ;
2016-10-01 20:42:39 +02:00
_contactsManager = contactsManager ;
2016-09-23 22:55:56 +02:00
_contactsUpdater = contactsUpdater ;
2017-06-22 03:04:16 +02:00
_identityManager = identityManager ;
2016-10-14 23:00:29 +02:00
_messageSender = messageSender ;
2016-09-23 22:55:56 +02:00
2016-10-05 17:42:44 +02:00
_dbConnection = storageManager . newDatabaseConnection ;
2017-02-16 00:32:27 +01:00
_incomingMessageFinder = [ [ OWSIncomingMessageFinder alloc ] initWithDatabase : storageManager . database ] ;
2017-04-03 20:42:04 +02:00
_blockingManager = [ OWSBlockingManager sharedManager ] ;
2017-04-01 00:36:08 +02:00
OWSSingletonAssert ( ) ;
2017-06-30 18:12:37 +02:00
[ self startObserving ] ;
2015-12-07 03:31:43 +01:00
return self ;
}
2017-06-30 18:12:37 +02:00
- ( void ) startObserving
{
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( yapDatabaseModified : )
name : YapDatabaseModifiedNotification
object : nil ] ;
}
- ( void ) yapDatabaseModified : ( NSNotification * ) notification
{
[ self updateApplicationBadgeCount ] ;
}
2017-04-25 21:42:49 +02:00
# pragma mark - Debugging
2016-10-14 23:00:29 +02:00
2017-07-21 21:38:44 +02:00
- ( NSString * ) descriptionForEnvelopeType : ( OWSSignalServiceProtosEnvelope * ) envelope
2017-03-23 20:21:46 +01:00
{
OWSAssert ( envelope ! = nil ) ;
2017-07-24 16:22:12 +02:00
2017-03-23 20:21:46 +01:00
switch ( envelope . type ) {
case OWSSignalServiceProtosEnvelopeTypeReceipt :
2017-07-21 21:38:44 +02:00
return @ "DeliveryReceipt" ;
2017-03-23 20:21:46 +01:00
case OWSSignalServiceProtosEnvelopeTypeUnknown :
// Shouldn ' t happen
2017-07-27 18:29:05 +02:00
OWSProdFail ( [ OWSAnalyticsEvents messageManagerErrorEnvelopeTypeUnknown ] ) ;
2017-07-21 21:38:44 +02:00
return @ "Unknown" ;
2017-03-23 20:21:46 +01:00
case OWSSignalServiceProtosEnvelopeTypeCiphertext :
2017-07-21 21:38:44 +02:00
return @ "SignalEncryptedMessage" ;
2017-03-23 20:21:46 +01:00
case OWSSignalServiceProtosEnvelopeTypeKeyExchange :
// Unsupported
2017-07-27 18:29:05 +02:00
OWSProdFail ( [ OWSAnalyticsEvents messageManagerErrorEnvelopeTypeKeyExchange ] ) ;
2017-07-21 21:38:44 +02:00
return @ "KeyExchange" ;
2017-03-23 20:21:46 +01:00
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle :
2017-07-21 21:38:44 +02:00
return @ "PreKeyEncryptedMessage" ;
2017-03-23 20:21:46 +01:00
default :
// Shouldn ' t happen
2017-07-27 18:29:05 +02:00
OWSProdFail ( [ OWSAnalyticsEvents messageManagerErrorEnvelopeTypeOther ] ) ;
2017-07-21 21:38:44 +02:00
return @ "Other" ;
2017-03-23 20:21:46 +01:00
}
2017-07-21 21:38:44 +02:00
}
- ( NSString * ) descriptionForEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope
{
OWSAssert ( envelope ! = nil ) ;
2017-03-23 20:21:46 +01:00
2017-04-25 21:42:49 +02:00
return [ NSString stringWithFormat : @ "<Envelope type: %@, source: %@.%d, timestamp: %llu content.length: %lu />" ,
2017-07-21 21:38:44 +02:00
[ self descriptionForEnvelopeType : envelope ] ,
2017-03-23 20:21:46 +01:00
envelope . source ,
2017-04-25 21:42:49 +02:00
( unsigned int ) envelope . sourceDevice ,
2017-03-23 20:21:46 +01:00
envelope . timestamp ,
( unsigned long ) envelope . content . length ] ;
}
2017-04-25 21:42:49 +02:00
/ * *
* We don ' t want to just log ` content . description` because we ' d potentially log message bodies for dataMesssages and
* sync transcripts
* /
- ( NSString * ) descriptionForContent : ( OWSSignalServiceProtosContent * ) content
{
if ( content . hasSyncMessage ) {
return [ NSString stringWithFormat : @ "<SyncMessage: %@ />" , [ self descriptionForSyncMessage : content . syncMessage ] ] ;
} else if ( content . hasDataMessage ) {
return [ NSString stringWithFormat : @ "<DataMessage: %@ />" , [ self descriptionForDataMessage : content . dataMessage ] ] ;
} else if ( content . hasCallMessage ) {
return [ NSString stringWithFormat : @ "<CallMessage: %@ />" , content . callMessage ] ;
2017-06-22 18:12:24 +02:00
} else if ( content . hasNullMessage ) {
return [ NSString stringWithFormat : @ "<NullMessage: %@ />" , content . nullMessage ] ;
2017-04-25 21:42:49 +02:00
} else {
2017-07-21 21:38:44 +02:00
// Don ' t fire an analytics event ; if we ever add a new content type , we ' d generate a ton of
// analytics traffic .
OWSFail ( @ "Unknown content type." ) ;
2017-04-25 21:42:49 +02:00
return @ "UnknownContent" ;
}
}
/ * *
* We don ' t want to just log ` dataMessage . description` because we ' d potentially log message contents
* /
- ( NSString * ) descriptionForDataMessage : ( OWSSignalServiceProtosDataMessage * ) dataMessage
{
NSMutableString * description = [ NSMutableString new ] ;
if ( dataMessage . hasGroup ) {
[ description appendString : @ "GroupDataMessage: " ] ;
} else {
[ description appendString : @ "DataMessage: " ] ;
}
if ( ( dataMessage . flags & OWSSignalServiceProtosDataMessageFlagsEndSession ) ! = 0 ) {
[ description appendString : @ "EndSession" ] ;
} else if ( ( dataMessage . flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate ) ! = 0 ) {
[ description appendString : @ "ExpirationTimerUpdate" ] ;
} else if ( dataMessage . attachments . count > 0 ) {
[ description appendString : @ "MessageWithAttachment" ] ;
} else {
[ description appendString : @ "Plain" ] ;
}
return [ NSString stringWithFormat : @ "<%@ />" , description ] ;
}
/ * *
* We don ' t want to just log ` syncMessage . description` because we ' d potentially log message contents in sent transcripts
* /
- ( NSString * ) descriptionForSyncMessage : ( OWSSignalServiceProtosSyncMessage * ) syncMessage
{
2017-06-07 16:11:01 +02:00
NSMutableString * description = [ NSMutableString new ] ;
2017-04-25 21:42:49 +02:00
if ( syncMessage . hasSent ) {
[ description appendString : @ "SentTranscript" ] ;
} else if ( syncMessage . hasRequest ) {
if ( syncMessage . request . type = = OWSSignalServiceProtosSyncMessageRequestTypeContacts ) {
[ description appendString : @ "ContactRequest" ] ;
} else if ( syncMessage . request . type = = OWSSignalServiceProtosSyncMessageRequestTypeGroups ) {
[ description appendString : @ "GroupRequest" ] ;
2017-07-21 21:38:44 +02:00
} else if ( syncMessage . request . type = = OWSSignalServiceProtosSyncMessageRequestTypeBlocked ) {
[ description appendString : @ "BlockedRequest" ] ;
2017-04-25 21:42:49 +02:00
} else {
// Shouldn ' t happen
2017-07-21 21:38:44 +02:00
OWSFail ( @ "Unknown sync message request type" ) ;
2017-04-25 21:42:49 +02:00
[ description appendString : @ "UnknownRequest" ] ;
}
} else if ( syncMessage . hasBlocked ) {
[ description appendString : @ "Blocked" ] ;
} else if ( syncMessage . read . count > 0 ) {
[ description appendString : @ "ReadReceipt" ] ;
2017-06-22 02:19:23 +02:00
} else if ( syncMessage . hasVerified ) {
NSString * verifiedString =
[ NSString stringWithFormat : @ "Verification for: %@" , syncMessage . verified . destination ] ;
2017-06-19 16:55:05 +02:00
[ description appendString : verifiedString ] ;
2017-04-25 21:42:49 +02:00
} else {
// Shouldn ' t happen
2017-07-21 21:38:44 +02:00
OWSFail ( @ "Unknown sync message type" ) ;
2017-04-25 21:42:49 +02:00
[ description appendString : @ "Unknown" ] ;
}
return description ;
}
# pragma mark - message handling
2017-07-25 00:05:26 +02:00
- ( void ) processEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope
completion : ( nullable MessageManagerCompletionBlock ) completionHandler
2016-08-22 22:09:58 +02:00
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-08-04 18:01:45 +02:00
OWSAssert ( [ TSAccountManager isRegistered ] ) ;
2017-03-23 20:21:46 +01:00
2017-04-14 19:37:04 +02:00
// Ensure that completionHandler is called on the main thread ,
// and handle the nil case .
MessageManagerCompletionBlock completion = ^ {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( completionHandler ) {
completionHandler ( ) ;
}
} ) ;
} ;
2017-03-23 20:21:46 +01:00
DDLogInfo ( @ "%@ received envelope: %@" , self . tag , [ self descriptionForEnvelope : envelope ] ) ;
2017-04-01 17:25:14 +02:00
2017-04-01 22:47:16 +02:00
OWSAssert ( envelope . source . length > 0 ) ;
2017-04-01 17:25:14 +02:00
BOOL isEnvelopeBlocked = [ _blockingManager . blockedPhoneNumbers containsObject : envelope . source ] ;
if ( isEnvelopeBlocked ) {
DDLogInfo ( @ "%@ ignoring blocked envelope: %@" , self . tag , envelope . source ) ;
2017-04-14 19:37:04 +02:00
completion ( ) ;
2017-04-01 17:25:14 +02:00
return ;
}
2015-12-07 03:31:43 +01:00
@ try {
2016-08-22 22:09:58 +02:00
switch ( envelope . type ) {
2017-01-24 21:47:41 +01:00
case OWSSignalServiceProtosEnvelopeTypeCiphertext : {
[ self handleSecureMessageAsync : envelope
completion : ^ ( NSError * _Nullable error ) {
DDLogDebug ( @ "%@ handled secure message." , self . tag ) ;
if ( error ) {
DDLogError (
2017-05-30 22:04:10 +02:00
@ "%@ handling secure message from address: %@.%d failed with error: %@" ,
self . tag ,
envelope . source ,
( unsigned int ) envelope . sourceDevice ,
error ) ;
2017-07-27 18:29:05 +02:00
OWSProdError (
[ OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage ] ) ;
2017-01-24 21:47:41 +01:00
}
2017-04-14 19:37:04 +02:00
completion ( ) ;
2017-01-24 21:47:41 +01:00
} ] ;
2017-04-14 19:37:04 +02:00
// Return to avoid double - acknowledging .
return ;
2017-01-24 21:47:41 +01:00
}
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle : {
[ self handlePreKeyBundleAsync : envelope
completion : ^ ( NSError * _Nullable error ) {
2017-05-30 22:04:10 +02:00
DDLogDebug ( @ "%@ handled pre-key whisper message" , self . tag ) ;
2017-01-24 21:47:41 +01:00
if ( error ) {
2017-05-30 22:04:10 +02:00
DDLogError ( @ "%@ handling pre-key whisper message from address: %@.%d failed "
@ "with error: %@" ,
self . tag ,
envelope . source ,
( unsigned int ) envelope . sourceDevice ,
error ) ;
2017-07-27 18:29:05 +02:00
OWSProdError (
[ OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle ] ) ;
2017-01-24 21:47:41 +01:00
}
2017-04-14 19:37:04 +02:00
completion ( ) ;
2017-01-24 21:47:41 +01:00
} ] ;
2017-04-14 19:37:04 +02:00
// Return to avoid double - acknowledging .
return ;
2017-01-24 21:47:41 +01:00
}
2016-08-22 22:09:58 +02:00
case OWSSignalServiceProtosEnvelopeTypeReceipt :
[ 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 ) {
2017-04-14 19:37:04 +02:00
DDLogError ( @ "Received an incorrectly formatted protocol buffer: %@" , exception . debugDescription ) ;
2017-07-27 18:29:05 +02:00
OWSProdFail ( [ OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage ] ) ;
2015-12-07 03:31:43 +01:00
}
2017-04-14 19:37:04 +02:00
completion ( ) ;
2015-12-07 03:31:43 +01:00
}
2016-08-22 22:09:58 +02:00
- ( void ) handleDeliveryReceipt : ( OWSSignalServiceProtosEnvelope * ) envelope
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-08-04 18:01:45 +02:00
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 ;
2017-04-11 22:57:28 +02:00
[ outgoingMessage updateWithWasDeliveredWithTransaction : transaction ] ;
2016-08-22 22:09:58 +02:00
}
2015-12-07 03:31:43 +01:00
} ] ;
}
2017-01-24 21:47:41 +01:00
- ( void ) handleSecureMessageAsync : ( OWSSignalServiceProtosEnvelope * ) messageEnvelope
completion : ( void ( ^ ) ( NSError * _Nullable error ) ) completion
2016-08-22 22:09:58 +02:00
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-08-04 18:01:45 +02:00
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 ;
2017-04-07 00:55:10 +02:00
dispatch_async ( [ OWSDispatch sessionStoreQueue ] , ^ {
// DEPRECATED - Remove after all clients have been upgraded .
NSData * encryptedData
= messageEnvelope . hasContent ? messageEnvelope . content : messageEnvelope . legacyMessage ;
if ( ! encryptedData ) {
2017-07-27 18:29:05 +02:00
OWSProdFail ( [ OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent ] ) ;
2017-04-14 19:37:04 +02:00
completion ( nil ) ;
2017-04-07 00:55:10 +02:00
return ;
}
2015-12-07 03:31:43 +01:00
2017-01-24 21:47:41 +01:00
NSData * plaintextData ;
@ try {
WhisperMessage * message = [ [ WhisperMessage alloc ] initWithData : encryptedData ] ;
SessionCipher * cipher = [ [ SessionCipher alloc ] initWithSessionStore : storageManager
preKeyStore : storageManager
signedPreKeyStore : storageManager
2017-06-22 03:04:16 +02:00
identityKeyStore : self . identityManager
2017-01-24 21:47:41 +01:00
recipientId : recipientId
deviceId : deviceId ] ;
plaintextData = [ [ cipher decrypt : message ] removePadding ] ;
} @ catch ( NSException * exception ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self processException : exception envelope : messageEnvelope ] ;
NSString * errorDescription =
[ NSString stringWithFormat : @ "Exception while decrypting: %@" , exception . description ] ;
NSError * error = OWSErrorWithCodeDescription ( OWSErrorCodeFailedToDecryptMessage , errorDescription ) ;
completion ( error ) ;
} ) ;
return ;
}
2016-08-22 22:09:58 +02:00
2017-01-24 21:47:41 +01:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self handleEnvelope : messageEnvelope plaintextData : plaintextData ] ;
completion ( nil ) ;
} ) ;
} ) ;
2015-12-07 03:31:43 +01:00
}
}
2017-01-24 21:47:41 +01:00
- ( void ) handlePreKeyBundleAsync : ( OWSSignalServiceProtosEnvelope * ) preKeyEnvelope
completion : ( void ( ^ ) ( NSError * _Nullable error ) ) completion
2016-08-22 22:09:58 +02:00
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-02-10 01:35:10 +01:00
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 ) {
2017-07-27 18:29:05 +02:00
OWSProdFail ( [ OWSAnalyticsEvents messageManagerErrorPrekeyBundleEnvelopeHasNoContent ] ) ;
2017-04-14 19:37:04 +02:00
completion ( nil ) ;
2016-08-22 22:09:58 +02:00
return ;
}
2015-12-07 03:31:43 +01:00
2017-04-06 22:29:12 +02:00
dispatch_async ( [ OWSDispatch sessionStoreQueue ] , ^ {
2017-01-24 21:47:41 +01:00
NSData * plaintextData ;
@ try {
2017-02-10 01:35:10 +01:00
// Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage .
2017-04-14 15:14:52 +02:00
[ TSPreKeyManager checkPreKeys ] ;
2017-02-10 01:35:10 +01:00
2017-01-24 21:47:41 +01:00
PreKeyWhisperMessage * message = [ [ PreKeyWhisperMessage alloc ] initWithData : encryptedData ] ;
SessionCipher * cipher = [ [ SessionCipher alloc ] initWithSessionStore : storageManager
preKeyStore : storageManager
signedPreKeyStore : storageManager
2017-06-22 03:04:16 +02:00
identityKeyStore : self . identityManager
2017-01-24 21:47:41 +01:00
recipientId : recipientId
deviceId : deviceId ] ;
plaintextData = [ [ cipher decrypt : message ] removePadding ] ;
} @ catch ( NSException * exception ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self processException : exception envelope : preKeyEnvelope ] ;
NSString * errorDescription = [ NSString stringWithFormat : @ "Exception while decrypting PreKey Bundle: %@" , exception . description ] ;
NSError * error = OWSErrorWithCodeDescription ( OWSErrorCodeFailedToDecryptMessage , errorDescription ) ;
completion ( error ) ;
} ) ;
return ;
}
2016-08-22 22:09:58 +02:00
2017-01-24 21:47:41 +01:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self handleEnvelope : preKeyEnvelope plaintextData : plaintextData ] ;
2017-04-14 19:37:04 +02:00
completion ( nil ) ;
2017-01-24 21:47:41 +01:00
} ) ;
} ) ;
2016-12-06 03:32:11 +01:00
}
}
- ( void ) handleEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope plaintextData : ( NSData * ) plaintextData
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-02-22 01:21:09 +01:00
OWSAssert ( envelope . hasTimestamp && envelope . timestamp > 0 ) ;
OWSAssert ( envelope . hasSource && envelope . source . length > 0 ) ;
OWSAssert ( envelope . hasSourceDevice && envelope . sourceDevice > 0 ) ;
2017-02-16 00:32:27 +01:00
BOOL duplicateEnvelope = [ self . incomingMessageFinder existsMessageWithTimestamp : envelope . timestamp
sourceId : envelope . source
sourceDeviceId : envelope . sourceDevice ] ;
if ( duplicateEnvelope ) {
2017-06-22 03:04:16 +02:00
DDLogInfo ( @ "%@ Ignoring previously received envelope from %@.%d with timestamp: %llu" , self . tag , envelope . source , ( unsigned int ) envelope . sourceDevice , envelope . timestamp ) ;
2017-02-16 00:32:27 +01:00
return ;
}
2016-12-06 03:32:11 +01:00
if ( envelope . hasContent ) {
OWSSignalServiceProtosContent * content = [ OWSSignalServiceProtosContent parseFromData : plaintextData ] ;
2017-04-25 21:42:49 +02:00
DDLogInfo ( @ "%@ handling content: <Content: %@>" , self . tag , [ self descriptionForContent : content ] ) ;
2017-08-01 19:53:51 +02:00
if ( [ content hasProfileKey ] ) {
NSData * profileKey = [ content profileKey ] ;
NSString * recipientId = envelope . source ;
2017-08-02 19:12:26 +02:00
id < ProfileManagerProtocol > profileManager = [ TextSecureKitEnv sharedEnv ] . profileManager ;
[ profileManager setProfileKey : profileKey forRecipientId : recipientId ] ;
2017-08-01 19:53:51 +02:00
}
2016-12-06 03:32:11 +01:00
if ( content . hasSyncMessage ) {
[ self handleIncomingEnvelope : envelope withSyncMessage : content . syncMessage ] ;
} else if ( content . hasDataMessage ) {
[ self handleIncomingEnvelope : envelope withDataMessage : content . dataMessage ] ;
2016-12-18 22:08:26 +01:00
} else if ( content . hasCallMessage ) {
[ self handleIncomingEnvelope : envelope withCallMessage : content . callMessage ] ;
2017-06-22 03:05:21 +02:00
} else if ( content . hasNullMessage ) {
DDLogInfo ( @ "%@ Received null message." , self . tag ) ;
2016-08-23 22:38:05 +02:00
} else {
2017-02-16 00:32:27 +01:00
DDLogWarn ( @ "%@ Ignoring envelope. Content with no known payload" , self . tag ) ;
2016-08-22 22:09:58 +02:00
}
2016-12-06 03:32:11 +01:00
} else if ( envelope . hasLegacyMessage ) { // DEPRECATED - Remove after all clients have been upgraded .
OWSSignalServiceProtosDataMessage * dataMessage =
[ OWSSignalServiceProtosDataMessage parseFromData : plaintextData ] ;
2017-04-25 21:42:49 +02:00
DDLogInfo ( @ "%@ handling dataMessage: %@" , self . tag , [ self descriptionForDataMessage : dataMessage ] ) ;
2016-12-06 03:32:11 +01:00
[ self handleIncomingEnvelope : envelope withDataMessage : dataMessage ] ;
} else {
2017-08-04 18:01:45 +02:00
OWSProdInfoWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorEnvelopeNoActionablePayload ] , envelope ) ;
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
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-05-05 23:22:56 +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
2017-05-12 18:39:36 +02:00
DDLogInfo ( @ "%@ Received message from group that I left or don't know about from: %@." ,
self . tag ,
incomingEnvelope . source ) ;
2017-05-05 23:22:56 +02:00
NSString * recipientId = incomingEnvelope . source ;
__block TSThread * thread ;
2017-07-05 22:58:39 +02:00
[ [ TSStorageManager sharedManager ] . dbReadWriteConnection
2017-05-10 16:05:01 +02:00
readWriteWithBlock : ^ ( YapDatabaseReadWriteTransaction * transaction ) {
2017-05-05 23:22:56 +02:00
thread = [ TSContactThread getOrCreateThreadWithContactId : recipientId transaction : transaction ] ;
} ] ;
NSData * groupId = dataMessage . group . id ;
OWSAssert ( groupId ) ;
OWSSyncGroupsRequestMessage * syncGroupsRequestMessage =
[ [ OWSSyncGroupsRequestMessage alloc ] initWithThread : thread groupId : groupId ] ;
[ self . messageSender sendMessage : syncGroupsRequestMessage
success : ^ {
2017-05-12 18:39:36 +02:00
DDLogWarn ( @ "%@ Successfully sent Request Group Info message." , self . tag ) ;
2017-05-05 23:22:56 +02:00
}
failure : ^ ( NSError * error ) {
DDLogError ( @ "%@ Failed to send Request Group Info message with error: %@" , self . tag , error ) ;
} ] ;
2015-12-07 03:31:43 +01:00
return ;
}
}
2016-08-22 22:09:58 +02:00
if ( ( dataMessage . flags & OWSSignalServiceProtosDataMessageFlagsEndSession ) ! = 0 ) {
[ self handleEndSessionMessageWithEnvelope : incomingEnvelope dataMessage : dataMessage ] ;
2016-10-05 17:42:44 +02:00
} else if ( ( dataMessage . flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate ) ! = 0 ) {
[ self handleExpirationTimerUpdateMessageWithEnvelope : incomingEnvelope dataMessage : dataMessage ] ;
2016-10-14 23:00:29 +02:00
} else if ( dataMessage . attachments . count > 0 ) {
2016-08-22 22:09:58 +02:00
[ self handleReceivedMediaWithEnvelope : incomingEnvelope dataMessage : dataMessage ] ;
2015-12-07 03:31:43 +01:00
} else {
2016-08-22 22:09:58 +02:00
[ self handleReceivedTextMessageWithEnvelope : incomingEnvelope dataMessage : dataMessage ] ;
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 ] ;
}
}
}
2016-12-18 22:08:26 +01:00
- ( void ) handleIncomingEnvelope : ( OWSSignalServiceProtosEnvelope * ) incomingEnvelope
withCallMessage : ( OWSSignalServiceProtosCallMessage * ) callMessage
{
if ( callMessage . hasOffer ) {
[ self . callMessageHandler receivedOffer : callMessage . offer fromCallerId : incomingEnvelope . source ] ;
} else if ( callMessage . hasAnswer ) {
[ self . callMessageHandler receivedAnswer : callMessage . answer fromCallerId : incomingEnvelope . source ] ;
} else if ( callMessage . iceUpdate . count > 0 ) {
for ( OWSSignalServiceProtosCallMessageIceUpdate * iceUpdate in callMessage . iceUpdate ) {
[ self . callMessageHandler receivedIceUpdate : iceUpdate fromCallerId : incomingEnvelope . source ] ;
}
} else if ( callMessage . hasHangup ) {
DDLogVerbose ( @ "%@ Received CallMessage with Hangup." , self . tag ) ;
[ self . callMessageHandler receivedHangup : callMessage . hangup fromCallerId : incomingEnvelope . source ] ;
} else if ( callMessage . hasBusy ) {
[ self . callMessageHandler receivedBusy : callMessage . busy fromCallerId : incomingEnvelope . source ] ;
} else {
2017-08-04 18:01:45 +02:00
OWSProdInfoWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorCallMessageNoActionablePayload ] , incomingEnvelope ) ;
2016-12-18 22:08:26 +01:00
}
}
2016-10-14 23:00:29 +02:00
- ( void ) handleReceivedGroupAvatarUpdateWithEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope
dataMessage : ( OWSSignalServiceProtosDataMessage * ) dataMessage
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2016-10-14 23:00:29 +02:00
TSGroupThread * groupThread = [ TSGroupThread getOrCreateThreadWithGroupIdData : dataMessage . group . id ] ;
2017-08-04 18:01:45 +02:00
OWSAssert ( groupThread ) ;
2016-10-14 23:00:29 +02:00
OWSAttachmentsProcessor * attachmentsProcessor =
[ [ OWSAttachmentsProcessor alloc ] initWithAttachmentProtos : @ [ dataMessage . group . avatar ]
timestamp : envelope . timestamp
relay : envelope . relay
thread : groupThread
networkManager : self . networkManager ] ;
if ( ! attachmentsProcessor . hasSupportedAttachments ) {
DDLogWarn ( @ "%@ received unsupported group avatar envelope" , self . tag ) ;
return ;
2015-12-07 03:31:43 +01:00
}
2016-10-14 23:00:29 +02:00
[ attachmentsProcessor fetchAttachmentsForMessage : nil
2017-05-10 16:05:01 +02:00
success : ^ ( TSAttachmentStream * attachmentStream ) {
2016-10-14 23:00:29 +02:00
[ groupThread updateAvatarWithAttachmentStream : attachmentStream ] ;
}
2017-05-10 16:05:01 +02:00
failure : ^ ( NSError * error ) {
2016-10-14 23:00:29 +02:00
DDLogError ( @ "%@ failed to fetch attachments for group avatar sent at: %llu. with error: %@" ,
self . tag ,
envelope . timestamp ,
error ) ;
} ] ;
}
- ( void ) handleReceivedMediaWithEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope
dataMessage : ( OWSSignalServiceProtosDataMessage * ) dataMessage
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2016-10-14 23:00:29 +02:00
TSThread * thread = [ self threadForEnvelope : envelope dataMessage : dataMessage ] ;
2017-08-04 18:01:45 +02:00
OWSAssert ( thread ) ;
2016-10-14 23:00:29 +02:00
OWSAttachmentsProcessor * attachmentsProcessor =
[ [ OWSAttachmentsProcessor alloc ] initWithAttachmentProtos : dataMessage . attachments
timestamp : envelope . timestamp
relay : envelope . relay
thread : thread
networkManager : self . networkManager ] ;
if ( ! attachmentsProcessor . hasSupportedAttachments ) {
DDLogWarn ( @ "%@ received unsupported media envelope" , self . tag ) ;
return ;
}
2017-05-10 16:05:01 +02:00
TSIncomingMessage * _Nullable createdMessage =
[ self handleReceivedEnvelope : envelope
withDataMessage : dataMessage
attachmentIds : attachmentsProcessor . supportedAttachmentIds ] ;
if ( ! createdMessage ) {
return ;
}
2016-10-14 23:00:29 +02:00
2017-06-13 16:50:42 +02:00
DDLogDebug ( @ "%@ incoming attachment message: %@" , self . tag , createdMessage . debugDescription ) ;
2016-10-14 23:00:29 +02:00
[ attachmentsProcessor fetchAttachmentsForMessage : createdMessage
2017-05-10 16:05:01 +02:00
success : ^ ( TSAttachmentStream * attachmentStream ) {
2016-10-14 23:00:29 +02:00
DDLogDebug (
@ "%@ successfully fetched attachment: %@ for message: %@" , self . tag , attachmentStream , createdMessage ) ;
}
2017-05-10 16:05:01 +02:00
failure : ^ ( NSError * error ) {
2016-10-14 23:00:29 +02:00
DDLogError (
@ "%@ failed to fetch attachments for message: %@ with error: %@" , self . tag , createdMessage , error ) ;
} ] ;
2015-12-07 03:31:43 +01:00
}
2016-08-23 22:38:05 +02:00
- ( void ) handleIncomingEnvelope : ( OWSSignalServiceProtosEnvelope * ) messageEnvelope
withSyncMessage : ( OWSSignalServiceProtosSyncMessage * ) syncMessage
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-08-04 18:01:45 +02:00
OWSAssert ( [ TSAccountManager isRegistered ] ) ;
NSString * localNumber = [ TSAccountManager localNumber ] ;
if ( ! [ localNumber isEqualToString : messageEnvelope . source ] ) {
// Sync messages should only come from linked devices .
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorSyncMessageFromUnknownSource ] , messageEnvelope ) ;
return ;
}
2016-08-23 22:38:05 +02:00
if ( syncMessage . hasSent ) {
OWSIncomingSentMessageTranscript * transcript =
[ [ OWSIncomingSentMessageTranscript alloc ] initWithProto : syncMessage . sent relay : messageEnvelope . relay ] ;
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 ] ) {
2017-05-10 16:05:01 +02:00
[ recordJob runWithAttachmentHandler : ^ ( TSAttachmentStream * attachmentStream ) {
2016-10-14 23:00:29 +02:00
TSGroupThread * groupThread =
[ TSGroupThread getOrCreateThreadWithGroupIdData : syncMessage . sent . message . group . id ] ;
[ groupThread updateAvatarWithAttachmentStream : attachmentStream ] ;
} ] ;
} else {
2017-05-10 16:05:01 +02:00
[ recordJob runWithAttachmentHandler : ^ ( TSAttachmentStream * attachmentStream ) {
2016-10-14 23:00:29 +02:00
DDLogDebug ( @ "%@ successfully fetched transcript attachment: %@" , self . tag , attachmentStream ) ;
} ] ;
}
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 ) {
OWSSyncContactsMessage * syncContactsMessage =
2017-06-22 03:04:16 +02:00
[ [ OWSSyncContactsMessage alloc ] initWithContactsManager : self . contactsManager
identityManager : self . identityManager ] ;
2017-03-31 02:04:19 +02:00
2016-10-14 23:00:29 +02:00
[ self . messageSender sendTemporaryAttachmentData : [ syncContactsMessage buildPlainTextAttachmentData ]
2017-03-31 02:04:19 +02:00
contentType : OWSMimeTypeApplicationOctetStream
inMessage : syncContactsMessage
success : ^ {
DDLogInfo ( @ "%@ Successfully sent Contacts response syncMessage." , self . tag ) ;
}
failure : ^ ( NSError * error ) {
DDLogError ( @ "%@ Failed to send Contacts response syncMessage with error: %@" , self . tag , error ) ;
} ] ;
2016-08-26 01:01:35 +02:00
} else if ( syncMessage . request . type = = OWSSignalServiceProtosSyncMessageRequestTypeGroups ) {
2016-08-27 00:07:54 +02:00
OWSSyncGroupsMessage * syncGroupsMessage = [ [ OWSSyncGroupsMessage alloc ] init ] ;
2017-03-31 02:04:19 +02:00
2016-10-14 23:00:29 +02:00
[ self . messageSender sendTemporaryAttachmentData : [ syncGroupsMessage buildPlainTextAttachmentData ]
2017-03-31 02:04:19 +02:00
contentType : OWSMimeTypeApplicationOctetStream
inMessage : syncGroupsMessage
success : ^ {
DDLogInfo ( @ "%@ Successfully sent Groups response syncMessage." , self . tag ) ;
}
failure : ^ ( NSError * error ) {
DDLogError ( @ "%@ Failed to send Groups response syncMessage with error: %@" , self . tag , error ) ;
} ] ;
2016-11-22 18:14:39 +01:00
} else {
DDLogWarn ( @ "%@ ignoring unsupported sync request message" , self . tag ) ;
2016-08-26 01:01:35 +02:00
}
2017-03-31 02:04:19 +02:00
} else if ( syncMessage . hasBlocked ) {
NSArray < NSString * > * blockedPhoneNumbers = [ syncMessage . blocked . numbers copy ] ;
2017-03-31 19:43:05 +02:00
[ _blockingManager setBlockedPhoneNumbers : blockedPhoneNumbers sendSyncMessage : NO ] ;
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 ] ;
2017-06-22 02:19:23 +02:00
} else if ( syncMessage . hasVerified ) {
DDLogInfo ( @ "%@ Received verification state for %@" , self . tag , syncMessage . verified . destination ) ;
2017-06-22 03:04:16 +02:00
[ self . identityManager processIncomingSyncMessage : syncMessage . verified ] ;
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
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
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
} ] ;
2017-04-07 01:23:55 +02:00
dispatch_async ( [ OWSDispatch sessionStoreQueue ] , ^ {
2017-06-08 22:18:45 +02:00
[ [ TSStorageManager sharedManager ] deleteAllSessionsForContact : endSessionEnvelope . source ] ;
2017-04-07 01:23:55 +02:00
} ) ;
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
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
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 ] ;
}
2017-08-04 18:01:45 +02:00
OWSAssert ( disappearingMessagesConfiguration ) ;
2016-10-05 17:42:44 +02:00
[ disappearingMessagesConfiguration save ] ;
2016-12-02 03:10:15 +01:00
NSString * name = [ self . contactsManager displayNameForPhoneIdentifier : envelope . source ] ;
2016-10-05 17:42:44 +02:00
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
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2016-10-05 17:42:44 +02:00
[ self handleReceivedEnvelope : textMessageEnvelope withDataMessage : dataMessage attachmentIds : @ [ ] ] ;
2015-12-07 03:31:43 +01:00
}
2017-05-05 23:22:56 +02:00
- ( void ) sendGroupUpdateForThread : ( TSGroupThread * ) gThread message : ( TSOutgoingMessage * ) message
{
OWSAssert ( [ NSThread isMainThread ] ) ;
OWSAssert ( gThread ) ;
2017-08-04 18:01:45 +02:00
OWSAssert ( gThread . groupModel ) ;
2017-05-05 23:22:56 +02:00
OWSAssert ( message ) ;
if ( gThread . groupModel . groupImage ) {
[ self . messageSender sendAttachmentData : UIImagePNGRepresentation ( gThread . groupModel . groupImage )
contentType : OWSMimeTypeImagePng
2017-05-15 15:53:16 +02:00
sourceFilename : nil
2017-05-05 23:22:56 +02:00
inMessage : message
success : ^ {
DDLogDebug ( @ "%@ Successfully sent group update with avatar" , self . tag ) ;
}
2017-05-10 16:05:01 +02:00
failure : ^ ( NSError * error ) {
2017-05-05 23:22:56 +02:00
DDLogError ( @ "%@ Failed to send group avatar update with error: %@" , self . tag , error ) ;
} ] ;
} else {
[ self . messageSender sendMessage : message
success : ^ {
DDLogDebug ( @ "%@ Successfully sent group update" , self . tag ) ;
}
2017-05-10 16:05:01 +02:00
failure : ^ ( NSError * error ) {
2017-05-05 23:22:56 +02:00
DDLogError ( @ "%@ Failed to send group update with error: %@" , self . tag , error ) ;
} ] ;
}
}
2017-05-12 17:38:24 +02:00
- ( void ) handleGroupInfoRequest : ( OWSSignalServiceProtosEnvelope * ) envelope
dataMessage : ( OWSSignalServiceProtosDataMessage * ) dataMessage
2017-05-10 16:05:01 +02:00
{
OWSAssert ( [ NSThread isMainThread ] ) ;
OWSAssert ( dataMessage . group . type = = OWSSignalServiceProtosGroupContextTypeRequestInfo ) ;
NSData * groupId = dataMessage . hasGroup ? dataMessage . group . id : nil ;
if ( ! groupId ) {
2017-07-21 21:38:44 +02:00
OWSFail ( @ "Group info request is missing group id." ) ;
2017-05-10 16:05:01 +02:00
return ;
}
2017-05-12 18:39:36 +02:00
DDLogWarn ( @ "%@ Received 'Request Group Info' message for group: %@ from: %@" , self . tag , groupId , envelope . source ) ;
2017-05-10 16:05:01 +02:00
[ self . dbConnection readWriteWithBlock : ^ ( YapDatabaseReadWriteTransaction * transaction ) {
TSGroupModel * emptyModelToFillOutId =
[ [ TSGroupModel alloc ] initWithTitle : nil memberIds : nil image : nil groupId : dataMessage . group . id ] ;
TSGroupThread * gThread = [ TSGroupThread threadWithGroupModel : emptyModelToFillOutId transaction : transaction ] ;
if ( ! gThread ) {
2017-05-12 18:39:36 +02:00
DDLogWarn ( @ "%@ Unknown group: %@" , self . tag , groupId ) ;
2017-05-10 16:05:01 +02:00
return ;
}
2017-05-12 18:39:36 +02:00
if ( ! [ gThread . groupModel . groupMemberIds containsObject : envelope . source ] ) {
DDLogWarn ( @ "%@ Ignoring 'Request Group Info' message for non-member of group. %@ not in %@" ,
self . tag ,
envelope . source ,
gThread . groupModel . groupMemberIds ) ;
}
2017-05-10 16:05:01 +02:00
NSString * updateGroupInfo =
[ gThread . groupModel getInfoStringAboutUpdateTo : gThread . groupModel contactsManager : self . contactsManager ] ;
TSOutgoingMessage * message = [ [ TSOutgoingMessage alloc ] initWithTimestamp : [ NSDate ows_millisecondTimeStamp ]
inThread : gThread
groupMetaMessage : TSGroupMessageUpdate ] ;
[ message updateWithCustomMessage : updateGroupInfo transaction : transaction ] ;
2017-05-12 17:38:24 +02:00
// Only send this group update to the requester .
[ message updateWithSingleGroupRecipient : envelope . source transaction : transaction ] ;
2017-05-10 16:05:01 +02:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self sendGroupUpdateForThread : gThread message : message ] ;
} ) ;
} ] ;
}
- ( TSIncomingMessage * _Nullable ) handleReceivedEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope
withDataMessage : ( OWSSignalServiceProtosDataMessage * ) dataMessage
attachmentIds : ( NSArray < NSString * > * ) attachmentIds
2016-07-29 21:33:25 +02:00
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
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
2017-02-09 00:17:24 +01:00
// Do this outside of a transaction to avoid deadlock
OWSAssert ( [ TSAccountManager isRegistered ] ) ;
NSString * localNumber = [ TSAccountManager localNumber ] ;
2017-05-10 16:05:01 +02:00
if ( dataMessage . group . type = = OWSSignalServiceProtosGroupContextTypeRequestInfo ) {
2017-05-12 17:38:24 +02:00
[ self handleGroupInfoRequest : envelope dataMessage : dataMessage ] ;
2017-05-10 16:05:01 +02:00
return nil ;
}
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-11-22 18:14:39 +01:00
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 : {
2016-12-02 03:10:15 +01:00
NSString * nameString = [ self . contactsManager displayNameForPhoneIdentifier : envelope . source ] ;
2016-11-22 18:14:39 +01:00
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
2017-02-16 00:32:27 +01:00
sourceDeviceId : envelope . sourceDevice
2016-11-22 18:14:39 +01:00
messageBody : body
attachmentIds : attachmentIds
expiresInSeconds : dataMessage . expireTimer ] ;
2017-06-13 16:50:42 +02:00
DDLogDebug ( @ "%@ incoming group text message: %@" , self . tag , incomingMessage . debugDescription ) ;
2016-11-22 18:14:39 +01:00
[ incomingMessage saveWithTransaction : transaction ] ;
break ;
}
default : {
2016-11-26 18:29:02 +01:00
DDLogWarn ( @ "%@ Ignoring unknown group message type:%d" , self . tag , ( int ) dataMessage . group . type ) ;
2016-11-22 18:14:39 +01:00
}
2015-12-07 03:31:43 +01:00
}
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 ]
2017-02-16 00:32:27 +01:00
sourceDeviceId : envelope . sourceDevice
2015-12-07 03:31:43 +01:00
messageBody : body
2016-10-05 17:42:44 +02:00
attachmentIds : attachmentIds
expiresInSeconds : dataMessage . expireTimer ] ;
2017-06-13 16:50:42 +02:00
DDLogDebug ( @ "%@ incoming 1:1 text message: %@" , self . tag , incomingMessage . debugDescription ) ;
[ incomingMessage saveWithTransaction : transaction ] ;
2015-12-07 03:31:43 +01:00
thread = cThread ;
}
if ( thread && incomingMessage ) {
2017-02-04 19:15:15 +01:00
// Any messages sent from the current user - from this device or another - should be
// automatically marked as read .
2017-02-09 00:17:24 +01:00
BOOL shouldMarkMessageAsRead = [ envelope . source isEqualToString : localNumber ] ;
2017-02-06 17:25:56 +01:00
if ( shouldMarkMessageAsRead ) {
2017-06-12 17:51:25 +02:00
// Don ' t send a read receipt for messages sent by ourselves .
2017-06-12 19:29:17 +02:00
[ incomingMessage markAsReadWithTransaction : transaction sendReadReceipt : NO updateExpiration : YES ] ;
2017-02-04 19:15:15 +01:00
}
2017-06-13 16:50:42 +02:00
DDLogDebug ( @ "%@ shouldMarkMessageAsRead: %d (%@)" , self . tag , shouldMarkMessageAsRead , envelope . source ) ;
2017-02-18 00:58:08 +01:00
// Other clients allow attachments to be sent along with body , we want the text displayed as a separate
// message
2016-07-29 21:33:25 +02:00
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 ;
2017-02-18 00:58:08 +01:00
TSIncomingMessage * textMessage = [ [ TSIncomingMessage alloc ] initWithTimestamp : textMessageTimestamp
inThread : thread
authorId : envelope . source
sourceDeviceId : envelope . sourceDevice
messageBody : body
attachmentIds : @ [ ]
expiresInSeconds : dataMessage . expireTimer ] ;
2017-06-13 16:50:42 +02:00
DDLogDebug ( @ "%@ incoming extra text message: %@" , self . tag , incomingMessage . debugDescription ) ;
2016-10-05 17:42:44 +02:00
[ 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
2017-05-09 20:38:49 +02:00
[ OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage : incomingMessage
contactsManager : self . contactsManager ] ;
2016-10-05 17:42:44 +02:00
// Update thread preview in inbox
[ thread touch ] ;
2016-09-13 16:15:01 +02:00
[ [ TextSecureKitEnv sharedEnv ] . notificationsManager notifyUserForIncomingMessage : incomingMessage
2016-12-18 22:08:26 +01:00
inThread : thread
contactsManager : self . contactsManager ] ;
2016-09-13 16:15:01 +02:00
}
2016-08-23 22:38:05 +02:00
return incomingMessage ;
2015-12-07 03:31:43 +01:00
}
2016-08-22 22:09:58 +02:00
- ( void ) processException : ( NSException * ) exception envelope : ( OWSSignalServiceProtosEnvelope * ) envelope
{
2017-01-24 21:47:41 +01:00
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-03-21 20:35:04 +01:00
DDLogError ( @ "%@ Got exception: %@ of type: %@ with reason: %@" ,
self . tag ,
exception . description ,
exception . name ,
exception . reason ) ;
2015-12-07 03:31:43 +01:00
2017-05-26 02:54:24 +02:00
__block TSErrorMessage * errorMessage ;
[ self . dbConnection readWriteWithBlock : ^ ( YapDatabaseReadWriteTransaction * transaction ) {
2015-12-07 03:31:43 +01:00
if ( [ exception . name isEqualToString : NoSessionException ] ) {
2017-08-04 18:01:45 +02:00
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorNoSession ] , envelope ) ;
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 ] ) {
2017-08-04 18:01:45 +02:00
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorInvalidKey ] , envelope ) ;
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 ] ) {
2017-08-04 18:01:45 +02:00
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorInvalidKeyId ] , envelope ) ;
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 ] ) {
2017-08-04 18:01:45 +02:00
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorInvalidMessageVersion ] , envelope ) ;
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 ] ) {
2017-06-08 05:21:25 +02:00
// Should no longer get here , since we now record the new identity for incoming messages .
2017-08-04 18:01:45 +02:00
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorUntrustedIdentityKeyException ] , envelope ) ;
2017-06-08 05:21:25 +02:00
OWSFail ( @ "%@ Failed to trust identity on incoming message from: %@.%d" ,
self . tag ,
envelope . source ,
envelope . sourceDevice ) ;
return ;
2015-12-07 03:31:43 +01:00
} else {
2017-08-04 18:01:45 +02:00
OWSProdErrorWEnvelope ( [ OWSAnalyticsEvents messageManagerErrorCorruptMessage ] , envelope ) ;
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 ] ;
} ] ;
2017-05-26 02:54:24 +02:00
if ( errorMessage ! = nil ) {
[ self notififyForErrorMessage : errorMessage withEnvelope : envelope ] ;
}
}
- ( void ) notififyForErrorMessage : ( TSErrorMessage * ) errorMessage withEnvelope : ( OWSSignalServiceProtosEnvelope * ) envelope
{
TSThread * contactThread = [ TSContactThread getOrCreateThreadWithContactId : envelope . source ] ;
[ [ TextSecureKitEnv sharedEnv ] . notificationsManager notifyUserForErrorMessage : errorMessage inThread : contactThread ] ;
2015-12-07 03:31:43 +01:00
}
2016-10-14 23:00:29 +02:00
# pragma mark - helpers
2015-12-07 03:31:43 +01:00
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
}
2017-07-25 00:05:26 +02:00
/ * *
* @ returns
* Group or Contact thread for message , creating a new one if necessary .
* /
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 ;
}
2017-06-30 18:12:37 +02:00
- ( void ) updateApplicationBadgeCount
{
NSUInteger numberOfItems = [ self unreadMessagesCount ] ;
[ [ UIApplication sharedApplication ] setApplicationIconBadgeNumber : numberOfItems ] ;
}
2015-12-07 03:31:43 +01:00
- ( NSUInteger ) unreadMessagesInThread : ( TSThread * ) thread {
__block NSUInteger numberOfItems ;
[ self . dbConnection readWithBlock : ^ ( YapDatabaseReadTransaction * transaction ) {
numberOfItems = [ [ transaction ext : TSUnreadDatabaseViewExtensionName ] numberOfItemsInGroup : thread . uniqueId ] ;
} ] ;
return numberOfItems ;
}
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