mirror of https://github.com/oxen-io/session-ios
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. // FREEBIEpull/1/head
parent
9093be2b0d
commit
fe7171dd93
@ -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
|
||||
@ -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
|
||||
@ -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…
Reference in New Issue