Sync messages with your other devices

After provisioning a desktop client, you'll see messages sent from your
phone appear on your desktop client, and messages sent from the desktop
client appear on your phone.

* In the process extracted some of the Attachment processing logic out
  of the giant MessagesManager.
* Some nullability annotations on affected files.

// FREEBIE
pull/1/head
Michael Kirk 9 years ago
parent 9093be2b0d
commit fe7171dd93

@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
relay:(nullable NSString *)relay
supportsVoice:(BOOL)voiceCapable;
+ (instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier;
+ (instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier
withTransaction:(YapDatabaseReadTransaction *)transaction;

@ -33,6 +33,15 @@ NS_ASSUME_NONNULL_BEGIN
return [self fetchObjectWithUniqueID:textSecureIdentifier transaction:transaction];
}
+ (instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier
{
__block SignalRecipient *recipient;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
recipient = [self recipientWithTextSecureIdentifier:textSecureIdentifier withTransaction:transaction];
}];
return recipient;
}
- (NSMutableOrderedSet *)devices {
return [_devices copy];
}

@ -4,6 +4,8 @@
#import "TSYapDatabaseObject.h"
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class TSInteraction;
@class TSInvalidIdentityKeyReceivingErrorMessage;
@ -27,13 +29,15 @@
*/
- (NSString *)name;
- (nullable NSString *)contactIdentifier;
#if TARGET_OS_IOS
/**
* Returns the image representing the thread. Nil if not available.
*
* @return UIImage of the thread, or nil.
*/
- (UIImage *)image;
- (nullable UIImage *)image;
#endif
#pragma mark Interactions
@ -89,7 +93,7 @@
*
* @return Last archival date.
*/
- (NSDate *)archivalDate;
- (nullable NSDate *)archivalDate;
/**
* Archives a thread with the current date.
@ -134,3 +138,5 @@
- (void)setDraft:(NSString *)draftString transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

@ -9,6 +9,8 @@
#import "TSOutgoingMessage.h"
#import "TSStorageManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSThread ()
@property (nonatomic, retain) NSDate *creationDate;
@ -73,12 +75,19 @@
return FALSE;
}
// Override in ContactThread
- (nullable NSString *)contactIdentifier
{
return nil;
}
- (NSString *)name {
NSAssert(FALSE, @"Should be implemented in subclasses");
return nil;
}
- (UIImage *)image {
- (nullable UIImage *)image
{
return nil;
}
@ -215,7 +224,8 @@
#pragma mark Archival
- (NSDate *)archivalDate {
- (nullable NSDate *)archivalDate
{
return _archivalDate;
}
@ -253,3 +263,5 @@
}
@end
NS_ASSUME_NONNULL_END

@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface TSContactThread : TSThread
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId;
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@ -16,8 +18,6 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)contactIdentifier;
- (NSString *)contactIdentifier;
@end
NS_ASSUME_NONNULL_END

@ -4,6 +4,7 @@
#import "TSContactThread.h"
#import "ContactsUpdater.h"
#import "TextSecureKitEnv.h"
#import <YapDatabase/YapDatabaseConnection.h>
#import <YapDatabase/YapDatabaseTransaction.h>
NS_ASSUME_NONNULL_BEGIN
@ -55,6 +56,16 @@ NS_ASSUME_NONNULL_BEGIN
return thread;
}
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
{
__block TSContactThread *thread;
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
thread = [self getOrCreateThreadWithContactId:contactId transaction:transaction];
}];
return thread;
}
- (NSString *)contactIdentifier {
return [[self class] contactIdFromThreadId:self.uniqueId];
}

@ -10,11 +10,15 @@
#import "TSThread.h"
@interface TSGroupThread : TSThread
@property (nonatomic, strong) TSGroupModel *groupModel;
+ (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel
transaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (instancetype)getOrCreateThreadWithGroupIdData:(NSData *)groupId;
+ (instancetype)fetchWithGroupIdData:(NSData *)groupId;
+ (instancetype)threadWithGroupModel:(TSGroupModel *)groupModel transaction:(YapDatabaseReadTransaction *)transaction;
- (NSData *)groupId;
@end

@ -10,19 +10,51 @@
#define TSGroupThreadPrefix @"g"
- (instancetype)initWithGroupModel:(TSGroupModel *)groupModel {
- (instancetype)initWithGroupModel:(TSGroupModel *)groupModel
{
NSString *uniqueIdentifier = [[self class] threadIdFromGroupId:groupModel.groupId];
self = [super initWithUniqueId:uniqueIdentifier];
if (!self) {
return self;
}
self = [super initWithUniqueId:uniqueIdentifier];
_groupModel = groupModel;
return self;
}
- (instancetype)initWithGroupIdData:(NSData *)groupId
{
TSGroupModel *groupModel = [[TSGroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:groupId];
self = [self initWithGroupModel:groupModel];
if (!self) {
return self;
}
return self;
}
+ (instancetype)threadWithGroupModel:(TSGroupModel *)groupModel transaction:(YapDatabaseReadTransaction *)transaction {
+ (instancetype)threadWithGroupModel:(TSGroupModel *)groupModel transaction:(YapDatabaseReadTransaction *)transaction
{
return [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupModel.groupId] transaction:transaction];
}
+ (instancetype)fetchWithGroupIdData:(NSData *)groupId
{
return [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupId]];
}
+ (instancetype)getOrCreateThreadWithGroupIdData:(NSData *)groupId
{
TSGroupThread *thread = [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupId]];
if (!thread) {
thread = [[self alloc] initWithGroupIdData:groupId];
[thread save];
}
return thread;
}
+ (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel
transaction:(YapDatabaseReadWriteTransaction *)transaction {
TSGroupThread *thread =
@ -35,24 +67,24 @@
return thread;
}
- (BOOL)isGroupThread {
return true;
}
- (NSData *)groupId {
return [[self class] groupIdFromThreadId:self.uniqueId];
+ (NSString *)threadIdFromGroupId:(NSData *)groupId
{
return [TSGroupThreadPrefix stringByAppendingString:[groupId base64EncodedString]];
}
- (NSString *)name {
return self.groupModel.groupName;
+ (NSData *)groupIdFromThreadId:(NSString *)threadId
{
return [NSData dataFromBase64String:[threadId substringWithRange:NSMakeRange(1, threadId.length - 1)]];
}
+ (NSString *)threadIdFromGroupId:(NSData *)groupId {
return [TSGroupThreadPrefix stringByAppendingString:[groupId base64EncodedString]];
- (BOOL)isGroupThread
{
return true;
}
+ (NSData *)groupIdFromThreadId:(NSString *)threadId {
return [NSData dataFromBase64String:[threadId substringWithRange:NSMakeRange(1, threadId.length - 1)]];
- (NSString *)name
{
return self.groupModel.groupName;
}
@end

@ -0,0 +1,30 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSThread;
@class TSMessagesManager;
@class OWSSignalServiceProtosAttachmentPointer;
/**
* Given incoming attachment protos, determines which we support.
* It can download those that we support and notifies threads when it receives unsupported attachments.
*/
@interface OWSAttachmentsProcessor : NSObject
@property (nonatomic, readonly) NSArray<NSString *> *attachmentIds;
@property (nonatomic, readonly) NSArray<NSString *> *supportedAttachmentIds;
@property (nonatomic, readonly) BOOL hasSupportedAttachments;
- (instancetype)initWithAttachmentPointersProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos
timestamp:(uint64_t)timestamp
relay:(nullable NSString *)relay
avatarGroupId:(nullable NSData *)avatarGroupId
inThread:(TSThread *)thread
messagesManager:(TSMessagesManager *)messagesManager;
- (void)fetchAttachmentsForMessageId:(nullable NSString *)messageId;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,92 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSAttachmentsProcessor.h"
#import "MIMETypeUtil.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSAttachmentPointer.h"
#import "TSInfoMessage.h"
#import "TSMessage.h"
#import "TSMessagesManager+attachments.h"
#import "TSMessagesManager.h"
#import "TSThread.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSAttachmentsProcessor ()
@property (nonatomic, readonly) TSMessagesManager *messagesManager;
@property (nonatomic, readonly) NSArray<TSAttachmentPointer *> *supportedAttachmentPointers;
@end
@implementation OWSAttachmentsProcessor
- (instancetype)initWithAttachmentPointersProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos
timestamp:(uint64_t)timestamp
relay:(nullable NSString *)relay
avatarGroupId:(nullable NSData *)avatarGroupId
inThread:(TSThread *)thread
messagesManager:(TSMessagesManager *)messagesManager;
{
self = [super init];
if (!self) {
return self;
}
_messagesManager = messagesManager;
NSMutableArray<NSString *> *attachmentIds = [NSMutableArray new];
NSMutableArray<TSAttachmentPointer *> *supportedAttachmentPointers = [NSMutableArray new];
NSMutableArray<NSString *> *supportedAttachmentIds = [NSMutableArray new];
for (OWSSignalServiceProtosAttachmentPointer *attachmentProto in attachmentProtos) {
TSAttachmentPointer *pointer;
if (avatarGroupId) {
pointer = [[TSAttachmentPointer alloc] initWithIdentifier:attachmentProto.id
key:attachmentProto.key
contentType:attachmentProto.contentType
relay:relay
avatarOfGroupId:avatarGroupId];
} else {
pointer = [[TSAttachmentPointer alloc] initWithIdentifier:attachmentProto.id
key:attachmentProto.key
contentType:attachmentProto.contentType
relay:relay];
}
[attachmentIds addObject:pointer.uniqueId];
if ([MIMETypeUtil isSupportedMIMEType:pointer.contentType]) {
[pointer save];
[supportedAttachmentPointers addObject:pointer];
[supportedAttachmentIds addObject:pointer.uniqueId];
} else {
TSInfoMessage *infoMessage = [[TSInfoMessage alloc] initWithTimestamp:timestamp
inThread:thread
messageType:TSInfoMessageTypeUnsupportedMessage];
[infoMessage save];
}
}
_attachmentIds = [attachmentIds copy];
_supportedAttachmentPointers = [supportedAttachmentPointers copy];
_supportedAttachmentIds = [supportedAttachmentIds copy];
return self;
}
- (void)fetchAttachmentsForMessageId:(nullable NSString *)messageId
{
for (TSAttachmentPointer *attachmentPointer in self.supportedAttachmentPointers) {
[self.messagesManager retrieveAttachment:attachmentPointer messageId:messageId];
}
}
- (BOOL)hasSupportedAttachments
{
return self.supportedAttachmentPointers.count > 0;
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,19 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingSyncMessage.h"
NS_ASSUME_NONNULL_BEGIN
@class TSOutgoingMessage;
/**
* Notifies your other registered devices (if you have any) that you've sent a message.
* This way the message you just sent can appear on all your devices.
*/
@interface OWSOutgoingSentMessageTranscript : OWSOutgoingSyncMessage
- (instancetype)initWithOutgoingMessage:(TSOutgoingMessage *)message;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,65 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingSentMessageTranscript.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSOutgoingMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSOutgoingMessage (OWSOutgoingSentMessageTranscript)
/**
* Normally this is private, but we need to embed this
* data structure within our own.
*/
- (OWSSignalServiceProtosDataMessage *)buildDataMessage;
@end
@interface OWSOutgoingSentMessageTranscript ()
@property (nonatomic, readonly) TSOutgoingMessage *message;
@end
@implementation OWSOutgoingSentMessageTranscript
- (instancetype)initWithOutgoingMessage:(TSOutgoingMessage *)message
{
self = [super init];
if (!self) {
return self;
}
_message = message;
return self;
}
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
OWSSignalServiceProtosSyncMessageSentBuilder *sentBuilder = [OWSSignalServiceProtosSyncMessageSentBuilder new];
[sentBuilder setTimestamp:self.message.timestamp];
[sentBuilder setDestination:self.message.recipientIdentifier];
OWSSignalServiceProtosDataMessage *dataMessage = [self.message buildDataMessage];
[sentBuilder setMessage:dataMessage];
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
[syncMessageBuilder setSent:[sentBuilder build]];
return [syncMessageBuilder build];
}
- (NSData *)buildPlainTextData
{
OWSSignalServiceProtosContentBuilder *contentBuilder = [OWSSignalServiceProtosContentBuilder new];
[contentBuilder setSyncMessage:[self buildSyncMessage]];
return [[contentBuilder build] data];
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,16 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "TSOutgoingMessage.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Abstract base class used for the family of sync messages which take care
* of keeping your multiple registered devices consistent. E.g. sharing contacts, sharing groups,
* notifiying your devices of sent messages, and "read" receipts.
*/
@interface OWSOutgoingSyncMessage : TSOutgoingMessage
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,16 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingSyncMessage.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSOutgoingSyncMessage
- (BOOL)shouldSyncTranscript
{
return NO;
}
@end
NS_ASSUME_NONNULL_END

@ -1,13 +1,12 @@
//
// TSOutgoingMessage.h
// TextSecureKit
//
// Created by Frederic Jacobs on 15/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "TSMessage.h"
NS_ASSUME_NONNULL_BEGIN
@class OWSSignalServiceProtosDataMessage;
@interface TSOutgoingMessage : TSMessage
typedef NS_ENUM(NSInteger, TSOutgoingMessageState) {
@ -18,4 +17,25 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) {
};
@property (nonatomic) TSOutgoingMessageState messageState;
@property BOOL hasSyncedTranscript;
/**
* Signal Identifier (e.g. e164 number) or nil if in a group thread.
*/
- (nullable NSString *)recipientIdentifier;
/**
* The data representation of this message, to be encrypted, before being sent.
*/
- (NSData *)buildPlainTextData;
/**
* Should this message be synced to the users other registered devices? This is
* generally always true, except in the case of the sync messages themseleves
* (so we don't end up in an infinite loop).
*/
- (BOOL)shouldSyncTranscript;
@end
NS_ASSUME_NONNULL_END

@ -2,8 +2,13 @@
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSOutgoingMessage.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
#import "TSGroupThread.h"
NS_ASSUME_NONNULL_BEGIN
@implementation TSOutgoingMessage
- (instancetype)initWithTimestamp:(uint64_t)timestamp
@ -18,6 +23,8 @@
}
_messageState = TSOutgoingMessageStateAttemptingOut;
_hasSyncedTranscript = NO;
if ([thread isKindOfClass:[TSGroupThread class]]) {
self.groupMetaMessage = TSGroupMessageDeliver;
} else {
@ -27,4 +34,85 @@
return self;
}
- (nullable NSString *)recipientIdentifier
{
return self.thread.contactIdentifier;
}
- (OWSSignalServiceProtosDataMessage *)buildDataMessage
{
TSThread *thread = self.thread;
OWSSignalServiceProtosDataMessageBuilder *builder = [OWSSignalServiceProtosDataMessageBuilder new];
[builder setBody:self.body];
BOOL processAttachments = YES;
if ([thread isKindOfClass:[TSGroupThread class]]) {
TSGroupThread *gThread = (TSGroupThread *)thread;
OWSSignalServiceProtosGroupContextBuilder *groupBuilder = [OWSSignalServiceProtosGroupContextBuilder new];
switch (self.groupMetaMessage) {
case TSGroupMessageQuit:
[groupBuilder setType:OWSSignalServiceProtosGroupContextTypeQuit];
break;
case TSGroupMessageUpdate:
case TSGroupMessageNew: {
if (gThread.groupModel.groupImage != nil && [self.attachmentIds count] == 1) {
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:self.attachmentIds[0]];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream *)dbObject;
OWSSignalServiceProtosAttachmentPointerBuilder *attachmentbuilder =
[OWSSignalServiceProtosAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[groupBuilder setAvatar:[attachmentbuilder build]];
processAttachments = NO;
}
}
[groupBuilder setMembersArray:gThread.groupModel.groupMemberIds];
[groupBuilder setName:gThread.groupModel.groupName];
[groupBuilder setType:OWSSignalServiceProtosGroupContextTypeUpdate];
break;
}
default:
[groupBuilder setType:OWSSignalServiceProtosGroupContextTypeDeliver];
break;
}
[groupBuilder setId:gThread.groupModel.groupId];
[builder setGroup:groupBuilder.build];
}
if (processAttachments) {
NSMutableArray *attachments = [NSMutableArray new];
for (NSString *attachmentId in self.attachmentIds) {
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream *)dbObject;
OWSSignalServiceProtosAttachmentPointerBuilder *attachmentbuilder =
[OWSSignalServiceProtosAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[attachments addObject:[attachmentbuilder build]];
}
}
[builder setAttachmentsArray:attachments];
}
return [builder build];
}
- (NSData *)buildPlainTextData
{
return [[self buildDataMessage] data];
}
- (BOOL)shouldSyncTranscript
{
return !self.hasSyncedTranscript;
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,22 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class OWSSignalServiceProtosSyncMessageSent;
/**
* Represents notification of a message sent on our behalf from another device.
* E.g. When we send a message from Signal-Desktop we want to see it in our conversation on iPhone.
*/
@interface OWSIncomingSentMessageTranscript : NSObject
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay;
/**
* Record an outgoing message based on the transcription
*/
- (void)record;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,99 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSIncomingSentMessageTranscript.h"
#import "OWSAttachmentsProcessor.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSMessagesManager.h"
#import "TSOutgoingMessage.h"
#import "TSThread.h"
// Thread finding imports
#import "TSContactThread.h"
#import "TSGroupModel.h"
#import "TSGroupThread.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSIncomingSentMessageTranscript ()
@property (nonatomic, readonly) NSString *relay;
@property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage;
@property (nonatomic, readonly) NSString *recipientId;
@property (nonatomic, readonly) uint64_t timestamp;
@end
@implementation OWSIncomingSentMessageTranscript
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay
{
self = [super init];
if (!self) {
return self;
}
_relay = relay;
_dataMessage = sentProto.message;
_recipientId = sentProto.destination;
_timestamp = sentProto.timestamp;
return self;
}
- (void)record
{
TSThread *thread;
if (self.dataMessage.hasGroup) {
thread = [TSGroupThread getOrCreateThreadWithGroupIdData:self.dataMessage.group.id];
} else {
thread = [TSContactThread getOrCreateThreadWithContactId:self.recipientId];
}
NSData *avatarGroupId;
NSArray<OWSSignalServiceProtosAttachmentPointer *> *attachmentPointerProtos;
if (self.dataMessage.hasGroup && (self.dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate)) {
avatarGroupId = self.dataMessage.group.id;
attachmentPointerProtos = @[ self.dataMessage.group.avatar ];
} else {
attachmentPointerProtos = self.dataMessage.attachments;
}
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:attachmentPointerProtos
timestamp:self.timestamp
relay:self.relay
avatarGroupId:avatarGroupId
inThread:thread
messagesManager:[TSMessagesManager sharedManager]];
// TODO group updates. Currently desktop doesn't support group updates, so not a problem yet.
TSOutgoingMessage *outgoingMessage =
[[TSOutgoingMessage alloc] initWithTimestamp:self.timestamp
inThread:thread
messageBody:self.dataMessage.body
attachmentIds:attachmentsProcessor.attachmentIds];
outgoingMessage.messageState = TSOutgoingMessageStateDelivered;
[outgoingMessage save];
[attachmentsProcessor fetchAttachmentsForMessageId:outgoingMessage.uniqueId];
// If there is an attachment + text, render the text here, as Signal-iOS renders two messages.
if (attachmentsProcessor.hasSupportedAttachments && self.dataMessage.body != nil
&& ![self.dataMessage.body isEqualToString:@""]) {
// render text *after* the attachment
uint64_t textMessageTimestamp = self.timestamp + 1000;
TSOutgoingMessage *textMessage = [[TSOutgoingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:thread
messageBody:self.dataMessage.body
attachmentIds:nil];
textMessage.messageState = TSOutgoingMessageStateDelivered;
[textMessage save];
}
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,20 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSMessageServiceParams.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Contstructs the per-device-message parameters used when submitting a message to
* the Signal Web Service. Using a legacy parameter format. Cannot be used for Sync messages.
*/
@interface OWSLegacyMessageServiceParams : OWSMessageServiceParams
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
body:(NSData *)body
registrationId:(int)registrationId;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,33 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSLegacyMessageServiceParams.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSLegacyMessageServiceParams
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
NSMutableDictionary *keys = [[super JSONKeyPathsByPropertyKey] mutableCopy];
[keys setObject:@"body" forKey:@"content"];
return [keys copy];
}
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
body:(NSData *)body
registrationId:(int)registrationId
{
self = [super initWithType:type recipientId:destination device:deviceId content:body registrationId:registrationId];
if (!self) {
return self;
}
return self;
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,25 @@
// Created by Frederic Jacobs on 18/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSConstants.h"
#import <Mantle/Mantle.h>
/**
* Contstructs the per-device-message parameters used when submitting a message to
* the Signal Web Service.
*/
@interface OWSMessageServiceParams : MTLModel <MTLJSONSerializing>
@property (nonatomic, readonly) int type;
@property (nonatomic, readonly) NSString *destination;
@property (nonatomic, readonly) int destinationDeviceId;
@property (nonatomic, readonly) int destinationRegistrationId;
@property (nonatomic, readonly) NSString *content;
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
content:(NSData *)content
registrationId:(int)registrationId;
@end

@ -0,0 +1,36 @@
// Created by Frederic Jacobs on 18/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "OWSMessageServiceParams.h"
#import "NSData+Base64.h"
#import "TSConstants.h"
@implementation OWSMessageServiceParams
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return [NSDictionary mtl_identityPropertyMapWithModel:[self class]];
}
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
content:(NSData *)content
registrationId:(int)registrationId
{
self = [super init];
if (!self) {
return self;
}
_type = type;
_destination = destination;
_destinationDeviceId = deviceId;
_destinationRegistrationId = registrationId;
_content = [content base64EncodedString];
return self;
}
@end

@ -4,6 +4,7 @@
#import "Cryptography.h"
#import "MIMETypeUtil.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSAttachmentsProcessor.h"
#import "TSAttachmentPointer.h"
#import "TSContactThread.h"
#import "TSGroupModel.h"
@ -34,64 +35,36 @@ dispatch_queue_t attachmentsQueue() {
- (void)handleReceivedMediaWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
// TODO extract group avatar handling rather than checking message type multiple times and forking logic.
NSArray *attachmentsToRetrieve
= (dataMessage.hasGroup && (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate))
? [NSArray arrayWithObject:dataMessage.group.avatar]
: dataMessage.attachments;
NSData *avatarGroupId;
NSArray<OWSSignalServiceProtosAttachmentPointer *> *attachmentPointerProtos;
if (dataMessage.hasGroup && (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate)) {
avatarGroupId = dataMessage.group.id;
attachmentPointerProtos = @[ dataMessage.group.avatar ];
} else {
attachmentPointerProtos = dataMessage.attachments;
}
NSMutableArray *retrievedAttachments = [NSMutableArray array];
__block BOOL shouldProcessMessage = YES;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (OWSSignalServiceProtosAttachmentPointer *pointer in attachmentsToRetrieve) {
TSAttachmentPointer *attachmentPointer
= (dataMessage.hasGroup && (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate))
? [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id
key:pointer.key
contentType:pointer.contentType
relay:envelope.relay
avatarOfGroupId:dataMessage.group.id]
: [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id
key:pointer.key
contentType:pointer.contentType
relay:envelope.relay];
if ([MIMETypeUtil isSupportedMIMEType:attachmentPointer.contentType]) {
[attachmentPointer saveWithTransaction:transaction];
[retrievedAttachments addObject:attachmentPointer.uniqueId];
shouldProcessMessage = YES;
} else {
TSThread *thread =
[TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
TSInfoMessage *infoMessage =
[[TSInfoMessage alloc] initWithTimestamp:envelope.timestamp
inThread:thread
messageType:TSInfoMessageTypeUnsupportedMessage];
[infoMessage saveWithTransaction:transaction];
shouldProcessMessage = NO;
}
}
}];
TSThread *thread;
if (dataMessage.hasGroup) {
thread = [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
} else {
thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
}
if (shouldProcessMessage) {
[self handleReceivedEnvelope:envelope
withDataMessage:dataMessage
attachmentIds:retrievedAttachments
completionBlock:^(NSString *messageIdentifier) {
for (NSString *pointerId in retrievedAttachments) {
dispatch_async(attachmentsQueue(), ^{
__block TSAttachmentPointer *pointer;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
pointer = [TSAttachmentPointer fetchObjectWithUniqueID:pointerId
transaction:transaction];
}];
[self retrieveAttachment:pointer messageId:messageIdentifier];
});
}
}];
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:attachmentPointerProtos
timestamp:envelope.timestamp
relay:envelope.relay
avatarGroupId:avatarGroupId
inThread:thread
messagesManager:[TSMessagesManager sharedManager]];
if (attachmentsProcessor.hasSupportedAttachments) {
TSIncomingMessage *possiblyCreatedMessage =
[self handleReceivedEnvelope:envelope
withDataMessage:dataMessage
attachmentIds:attachmentsProcessor.supportedAttachmentIds];
[attachmentsProcessor fetchAttachmentsForMessageId:possiblyCreatedMessage.uniqueId];
}
}
@ -253,9 +226,11 @@ dispatch_queue_t attachmentsQueue() {
[TSGroupThread getOrCreateThreadWithGroupModel:emptyModelToFillOutId transaction:transaction];
gThread.groupModel.groupImage = [stream image];
// No need to keep the attachment around after assigning the image.
// Avatars are stored directly in the database, so there's no need to keep the attachment around after
// assigning the image.
[stream removeWithTransaction:transaction];
[[TSMessage fetchObjectWithUniqueID:messageId] saveWithTransaction:transaction];
[gThread saveWithTransaction:transaction];
} else {
// Causing message to be reloaded in view.

@ -3,6 +3,9 @@
#import "ContactsUpdater.h"
#import "NSData+messagePadding.h"
#import "OWSLegacyMessageServiceParams.h"
#import "OWSMessageServiceParams.h"
#import "OWSOutgoingSentMessageTranscript.h"
#import "PreKeyBundle+jsonDict.h"
#import "TSAccountManager.h"
#import "TSAttachmentStream.h"
@ -11,7 +14,6 @@
#import "TSInfoMessage.h"
#import "TSMessagesManager+sendMessages.h"
#import "TSNetworkManager.h"
#import "TSServerMessage.h"
#import "TSStorageHeaders.h"
#import <AxolotlKit/AxolotlExceptions.h>
#import <AxolotlKit/SessionBuilder.h>
@ -228,13 +230,13 @@ dispatch_queue_t sendingQueue() {
[self saveMessage:message withState:TSOutgoingMessageStateUnsent];
}
- (void)sendMessage:(TSOutgoingMessage *)message
toRecipient:(SignalRecipient *)recipient
inThread:(TSThread *)thread
withAttemps:(int)remainingAttempts
success:(successSendingCompletionBlock)successBlock
failure:(failedSendingCompletionBlock)failureBlock {
failure:(failedSendingCompletionBlock)failureBlock
{
if (remainingAttempts > 0) {
remainingAttempts -= 1;
@ -247,11 +249,11 @@ dispatch_queue_t sendingQueue() {
[[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[recipient saveWithTransaction:transaction];
}];
[self handleMessageSent:message];
BLOCK_SAFE_RUN(successBlock);
dispatch_async(sendingQueue(), ^{
[recipient save];
[self handleMessageSent:message];
BLOCK_SAFE_RUN(successBlock);
});
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
@ -349,8 +351,31 @@ dispatch_queue_t sendingQueue() {
}];
}
- (void)handleMessageSent:(TSOutgoingMessage *)message {
- (void)handleMessageSent:(TSOutgoingMessage *)message
{
[self saveMessage:message withState:TSOutgoingMessageStateSent];
if (message.shouldSyncTranscript) {
message.hasSyncedTranscript = YES;
[self sendSyncTranscriptForMessage:message];
}
}
- (void)sendSyncTranscriptForMessage:(TSOutgoingMessage *)message
{
OWSOutgoingSentMessageTranscript *sentMessageTranscript =
[[OWSOutgoingSentMessageTranscript alloc] initWithOutgoingMessage:message];
SignalRecipient *localUser = [SignalRecipient recipientWithTextSecureIdentifier:[TSStorageManager localNumber]];
[self sendMessage:sentMessageTranscript
toRecipient:localUser
inThread:message.thread
withAttemps:RETRY_ATTEMPTS
success:^{
DDLogInfo(@"Succesfully sent sync transcript.");
}
failure:^{
DDLogInfo(@"Failed to send sync transcript.");
}];
}
- (NSArray<NSDictionary *> *)deviceMessages:(TSOutgoingMessage *)message
@ -358,15 +383,18 @@ dispatch_queue_t sendingQueue() {
inThread:(TSThread *)thread
{
NSMutableArray *messagesArray = [NSMutableArray arrayWithCapacity:recipient.devices.count];
TSStorageManager *storage = [TSStorageManager sharedManager];
NSData *plainText = [self plainTextForMessage:message inThread:thread];
NSData *plainText = [message buildPlainTextData];
for (NSNumber *deviceNumber in recipient.devices) {
@try {
// DEPRECATED - Remove after all clients have been upgraded.
BOOL isLegacyMessage = ![message isKindOfClass:[OWSOutgoingSyncMessage class]];
NSDictionary *messageDict = [self encryptedMessageWithPlaintext:plainText
toRecipient:recipient.uniqueId
deviceId:deviceNumber
keyingStorage:storage];
keyingStorage:[TSStorageManager sharedManager]
legacy:isLegacyMessage];
if (messageDict) {
[messagesArray addObject:messageDict];
} else {
@ -390,7 +418,9 @@ dispatch_queue_t sendingQueue() {
- (NSDictionary *)encryptedMessageWithPlaintext:(NSData *)plainText
toRecipient:(NSString *)identifier
deviceId:(NSNumber *)deviceNumber
keyingStorage:(TSStorageManager *)storage {
keyingStorage:(TSStorageManager *)storage
legacy:(BOOL)isLegacymessage
{
if (![storage containsSession:identifier deviceId:[deviceNumber intValue]]) {
__block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block PreKeyBundle *bundle;
@ -450,15 +480,24 @@ dispatch_queue_t sendingQueue() {
NSData *serializedMessage = encryptedMessage.serialized;
TSWhisperMessageType messageType = [self messageTypeForCipherMessage:encryptedMessage];
TSServerMessage *serverMessage = [[TSServerMessage alloc] initWithType:messageType
destination:identifier
device:[deviceNumber intValue]
body:serializedMessage
registrationId:cipher.remoteRegistrationId];
OWSMessageServiceParams *messageParams;
// DEPRECATED - Remove after all clients have been upgraded.
if (isLegacymessage) {
messageParams = [[OWSLegacyMessageServiceParams alloc] initWithType:messageType
recipientId:identifier
device:[deviceNumber intValue]
body:serializedMessage
registrationId:cipher.remoteRegistrationId];
} else {
messageParams = [[OWSMessageServiceParams alloc] initWithType:messageType
recipientId:identifier
device:[deviceNumber intValue]
content:serializedMessage
registrationId:cipher.remoteRegistrationId];
}
NSError *error;
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:serverMessage error:&error];
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];
if (error) {
DDLogError(@"Error while making JSON dictionary of message: %@", error.debugDescription);
@ -506,68 +545,6 @@ dispatch_queue_t sendingQueue() {
}
}
- (NSData *)plainTextForMessage:(TSOutgoingMessage *)message inThread:(TSThread *)thread {
OWSSignalServiceProtosDataMessageBuilder *builder = [OWSSignalServiceProtosDataMessageBuilder new];
[builder setBody:message.body];
BOOL processAttachments = YES;
if ([thread isKindOfClass:[TSGroupThread class]]) {
TSGroupThread *gThread = (TSGroupThread *)thread;
OWSSignalServiceProtosGroupContextBuilder *groupBuilder = [OWSSignalServiceProtosGroupContextBuilder new];
switch (message.groupMetaMessage) {
case TSGroupMessageQuit:
[groupBuilder setType:OWSSignalServiceProtosGroupContextTypeQuit];
break;
case TSGroupMessageUpdate:
case TSGroupMessageNew: {
if (gThread.groupModel.groupImage != nil && [message.attachmentIds count] == 1) {
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:message.attachmentIds[0]];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream *)dbObject;
OWSSignalServiceProtosAttachmentPointerBuilder *attachmentbuilder =
[OWSSignalServiceProtosAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[groupBuilder setAvatar:[attachmentbuilder build]];
processAttachments = NO;
}
}
[groupBuilder setMembersArray:gThread.groupModel.groupMemberIds];
[groupBuilder setName:gThread.groupModel.groupName];
[groupBuilder setType:OWSSignalServiceProtosGroupContextTypeUpdate];
break;
}
default:
[groupBuilder setType:OWSSignalServiceProtosGroupContextTypeDeliver];
break;
}
[groupBuilder setId:gThread.groupModel.groupId];
[builder setGroup:groupBuilder.build];
}
if (processAttachments) {
NSMutableArray *attachments = [NSMutableArray new];
for (NSString *attachmentId in message.attachmentIds) {
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream *)dbObject;
OWSSignalServiceProtosAttachmentPointerBuilder *attachmentbuilder =
[OWSSignalServiceProtosAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[attachments addObject:[attachmentbuilder build]];
}
}
[builder setAttachmentsArray:attachments];
}
return [builder.build data];
}
- (void)handleStaleDevicesWithResponse:(NSData *)responseData recipientId:(NSString *)identifier {
dispatch_async(sendingQueue(), ^{
NSDictionary *serialization = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];

@ -22,10 +22,16 @@
outgoingMessage:(TSOutgoingMessage *)message
inThread:(TSThread *)thread;
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds
completionBlock:(void (^)(NSString *messageIdentifier))completionBlock;
/**
* Processes all kinds of incoming envelopes with a data message, along with any attachments.
*
* @returns
* If an incoming message is created, it will be returned. If it is, for example, a group update,
* no incoming message is created, so nil will be returned.
*/
- (TSIncomingMessage *)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds;
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage;

@ -3,6 +3,7 @@
#import "TSMessagesManager.h"
#import "NSData+messagePadding.h"
#import "OWSIncomingSentMessageTranscript.h"
#import "TSAccountManager.h"
#import "TSAttachmentStream.h"
#import "TSCall.h"
@ -128,20 +129,20 @@
return;
}
OWSSignalServiceProtosDataMessage *dataMessage;
if (messageEnvelope.hasContent) { // New style content payload
if (messageEnvelope.hasContent) {
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
dataMessage = content.dataMessage;
if (content.hasSyncMessage) {
[self handleIncomingEnvelope:messageEnvelope withSyncMessage:content.syncMessage];
} else if (content.dataMessage) {
[self handleIncomingEnvelope:messageEnvelope withDataMessage:content.dataMessage];
}
} else if (messageEnvelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded.
dataMessage = [OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
OWSSignalServiceProtosDataMessage *dataMessage =
[OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
[self handleIncomingEnvelope:messageEnvelope withDataMessage:dataMessage];
} else {
DDLogWarn(@"Ignoring content that has no dataMessage or syncMessage.");
}
if (!dataMessage) {
DDLogWarn(@"Ignoring content that has no dataMessage.");
return;
}
[self handleIncomingEnvelope:messageEnvelope withDataMessage:dataMessage];
}
}
@ -175,23 +176,20 @@
return;
}
OWSSignalServiceProtosDataMessage *dataMessage;
if (preKeyEnvelope.hasContent) {
OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData];
if (content.hasDataMessage) {
dataMessage = content.dataMessage;
if (content.hasSyncMessage) {
[self handleIncomingEnvelope:preKeyEnvelope withSyncMessage:content.syncMessage];
} else if (content.dataMessage) {
[self handleIncomingEnvelope:preKeyEnvelope withDataMessage:content.dataMessage];
}
} else if (preKeyEnvelope.hasLegacyMessage) {
// DEPRECATED - Remove after all clients have been upgraded.
dataMessage = [OWSSignalServiceProtosDataMessage parseFromData:plaintextData];
} 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.");
}
if (!dataMessage) {
DDLogError(@"unable to ascertain decrypted dataMessage from preKeyEnvelope");
return;
}
[self handleIncomingEnvelope:preKeyEnvelope withDataMessage:dataMessage];
}
}
@ -229,6 +227,19 @@
}
}
- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)messageEnvelope
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
{
if (syncMessage.hasSent) {
DDLogInfo(@"Received sent message transcription");
OWSIncomingSentMessageTranscript *transcript =
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:messageEnvelope.relay];
[transcript record];
} else {
DDLogWarn(@"Ignoring unsupported sync message.");
}
}
- (void)handleEndSessionMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)endSessionEnvelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
@ -250,7 +261,7 @@
- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)textMessageEnvelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
[self handleReceivedEnvelope:textMessageEnvelope withDataMessage:dataMessage attachmentIds:@[] completionBlock:nil];
[self handleReceivedEnvelope:textMessageEnvelope withDataMessage:dataMessage attachmentIds:@[]];
}
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage
@ -267,17 +278,17 @@
}];
}
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds
completionBlock:(void (^)(NSString *messageIdentifier))completionBlock
- (TSIncomingMessage *)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds
{
uint64_t timeStamp = envelope.timestamp;
NSString *body = dataMessage.body;
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
__block TSIncomingMessage *incomingMessage;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSIncomingMessage *incomingMessage;
TSThread *thread;
if (groupId) {
NSMutableArray *uniqueMemberIds = [[[NSSet setWithArray:dataMessage.group.members] allObjects] mutableCopy];
@ -377,10 +388,6 @@
[incomingMessage saveWithTransaction:transaction];
}
if (completionBlock) {
completionBlock(incomingMessage.uniqueId);
}
NSString *name = [thread name];
if (incomingMessage && thread) {
@ -390,6 +397,8 @@
inThread:thread];
}
}];
return incomingMessage;
}
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope

@ -1,20 +0,0 @@
//
// TSServerMessage.h
// TextSecureKit
//
// Created by Frederic Jacobs on 18/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <Mantle/Mantle.h>
#import "TSConstants.h"
@interface TSServerMessage : MTLModel <MTLJSONSerializing>
- (instancetype)initWithType:(TSWhisperMessageType)type
destination:(NSString *)destination
device:(int)deviceId
body:(NSData *)body
registrationId:(int)registrationId;
@end

@ -1,48 +0,0 @@
//
// TSServerMessage.m
// TextSecureKit
//
// Created by Frederic Jacobs on 18/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "TSConstants.h"
#import "TSServerMessage.h"
#import "NSData+Base64.h"
@interface TSServerMessage ()
@property int type;
@property NSString *destination;
@property int destinationDeviceId;
@property int destinationRegistrationId;
@property NSString *body;
@end
@implementation TSServerMessage
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return [NSDictionary mtl_identityPropertyMapWithModel:[TSServerMessage class]];
}
- (instancetype)initWithType:(TSWhisperMessageType)type
destination:(NSString *)destination
device:(int)deviceId
body:(NSData *)body
registrationId:(int)registrationId {
self = [super init];
if (self) {
_type = type;
_destination = destination;
_destinationDeviceId = deviceId;
_destinationRegistrationId = registrationId;
_body = [body base64EncodedString];
}
return self;
}
@end
Loading…
Cancel
Save