From f1714bf2524fe1be71bc89f4769d2104e8115e05 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 7 Apr 2018 15:26:22 -0400 Subject: [PATCH] Handle synced quotes // FREEBIE --- .../src/Devices/OWSRecordTranscriptJob.m | 42 ++++- .../Attachments/OWSAttachmentsProcessor.h | 5 - .../Attachments/OWSAttachmentsProcessor.m | 31 +--- .../Attachments/TSAttachmentPointer.h | 6 +- .../Attachments/TSAttachmentPointer.m | 32 +++- .../OWSIncomingSentMessageTranscript.h | 15 +- .../OWSIncomingSentMessageTranscript.m | 23 +-- .../Messages/Interactions/TSQuotedMessage.h | 10 ++ .../Messages/Interactions/TSQuotedMessage.m | 148 ++++++++++++++++ .../src/Messages/OWSMessageManager.m | 159 ++---------------- 10 files changed, 265 insertions(+), 206 deletions(-) diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index 44f19d2c0..c132f3272 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -8,9 +8,11 @@ #import "OWSIncomingSentMessageTranscript.h" #import "OWSPrimaryStorage+SessionStore.h" #import "OWSReadReceiptManager.h" +#import "TSAttachmentPointer.h" #import "TSInfoMessage.h" #import "TSNetworkManager.h" #import "TSOutgoingMessage.h" +#import "TSQuotedMessage.h" #import "TextSecureKitEnv.h" NS_ASSUME_NONNULL_BEGIN @@ -65,12 +67,11 @@ NS_ASSUME_NONNULL_BEGIN OWSIncomingSentMessageTranscript *transcript = self.incomingSentMessageTranscript; DDLogDebug(@"%@ Recording transcript: %@", self.logTag, transcript); - TSThread *thread = [transcript threadWithTransaction:transaction]; if (transcript.isEndSessionMessage) { DDLogInfo(@"%@ EndSession was sent to recipient: %@.", self.logTag, transcript.recipientId); [self.primaryStorage deleteAllSessionsForContact:transcript.recipientId protocolContext:transaction]; [[[TSInfoMessage alloc] initWithTimestamp:transcript.timestamp - inThread:thread + inThread:transcript.thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; // Don't continue processing lest we print a bubble for the session reset. @@ -83,19 +84,50 @@ NS_ASSUME_NONNULL_BEGIN networkManager:self.networkManager transaction:transaction]; + // TODO group updates. Currently desktop doesn't support group updates, so not a problem yet. TSOutgoingMessage *outgoingMessage = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:transcript.timestamp - inThread:thread + inThread:transcript.thread messageBody:transcript.body attachmentIds:[attachmentsProcessor.attachmentIds mutableCopy] expiresInSeconds:transcript.expirationDuration expireStartedAt:transcript.expirationStartedAt isVoiceMessage:NO groupMetaMessage:TSGroupMessageNone - quotedMessage:nil]; + quotedMessage:transcript.quotedMessage]; + + TSQuotedMessage *_Nullable quotedMessage = transcript.quotedMessage; + if (quotedMessage && quotedMessage.thumbnailAttachmentPointerId) { + // We weren't able to derive a local thumbnail, so we'll fetch the referenced attachment. + TSAttachmentPointer *attachmentPointer = + [TSAttachmentPointer fetchObjectWithUniqueID:quotedMessage.thumbnailAttachmentPointerId + transaction:transaction]; + + if ([attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) { + OWSAttachmentsProcessor *attachmentProcessor = + [[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer + networkManager:self.networkManager]; + + DDLogDebug(@"%@ downloading thumbnail for transcript: %tu", self.logTag, transcript.timestamp); + [attachmentProcessor fetchAttachmentsForMessage:outgoingMessage + transaction:transaction + success:^(TSAttachmentStream *_Nonnull attachmentStream) { + [self.primaryStorage.newDatabaseConnection + asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [outgoingMessage setQuotedMessageThumbnailAttachmentStream:attachmentStream]; + [outgoingMessage saveWithTransaction:transaction]; + }]; + } + failure:^(NSError *_Nonnull error) { + DDLogWarn(@"%@ failed to fetch thumbnail for transcript: %tu with error: %@", + self.logTag, + transcript.timestamp, + error); + }]; + } + } - // TODO synced quoted replies if (transcript.isExpirationTimerUpdate) { [OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage contactsManager:self.contactsManager]; diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h index 8860ed2a3..c80335936 100644 --- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h +++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.h @@ -41,11 +41,6 @@ extern NSString *const kAttachmentDownloadAttachmentIDKey; - (instancetype)initWithAttachmentPointer:(TSAttachmentPointer *)attachmentPointer networkManager:(TSNetworkManager *)networkManager NS_DESIGNATED_INITIALIZER; - -+ (TSAttachmentPointer *)buildPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto - relay:(NSString *_Nullable)relay; - - - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message primaryStorage:(OWSPrimaryStorage *)primaryStorage success:(void (^)(TSAttachmentStream *attachmentStream))successHandler diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m index f61ed5088..0e1132786 100644 --- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m +++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m @@ -72,7 +72,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; NSMutableArray *attachmentPointers = [NSMutableArray new]; for (OWSSignalServiceProtosAttachmentPointer *attachmentProto in attachmentProtos) { - TSAttachmentPointer *pointer = [self.class buildPointerFromProto:attachmentProto relay:relay]; + TSAttachmentPointer *pointer = [TSAttachmentPointer attachmentPointerFromProto:attachmentProto relay:relay]; [attachmentIds addObject:pointer.uniqueId]; [pointer saveWithTransaction:transaction]; @@ -85,35 +85,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f; return self; } -+ (TSAttachmentPointer *)buildPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto - relay:(NSString *_Nullable)relay -{ - OWSAssert(attachmentProto.id != 0); - OWSAssert(attachmentProto.key != nil); - OWSAssert(attachmentProto.contentType != nil); - - // digest will be empty for old clients. - NSData *digest = attachmentProto.hasDigest ? attachmentProto.digest : nil; - - TSAttachmentType attachmentType = TSAttachmentTypeDefault; - if ([attachmentProto hasFlags]) { - UInt32 flags = attachmentProto.flags; - if ((flags & (UInt32)OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage) > 0) { - attachmentType = TSAttachmentTypeVoiceMessage; - } - } - - TSAttachmentPointer *pointer = [[TSAttachmentPointer alloc] initWithServerId:attachmentProto.id - key:attachmentProto.key - digest:digest - byteCount:attachmentProto.size - contentType:attachmentProto.contentType - relay:relay - sourceFilename:attachmentProto.fileName - attachmentType:attachmentType]; - return pointer; -} - // PERF: Remove this and use a pre-existing dbConnection - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message primaryStorage:(OWSPrimaryStorage *)primaryStorage diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h index 07dc290fb..ddd96cad7 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.h @@ -1,7 +1,8 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +#import "OWSSignalServiceProtos.pb.h" #import "TSAttachment.h" NS_ASSUME_NONNULL_BEGIN @@ -28,6 +29,9 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) { sourceFilename:(nullable NSString *)sourceFilename attachmentType:(TSAttachmentType)attachmentType NS_DESIGNATED_INITIALIZER; ++ (TSAttachmentPointer *)attachmentPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto + relay:(NSString *_Nullable)relay; + @property (nonatomic, readonly) NSString *relay; @property (atomic) TSAttachmentPointerState state; @property (nullable, atomic) NSString *mostRecentFailureLocalizedText; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m index de253d8d2..b1fbcead2 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentPointer.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "TSAttachmentPointer.h" @@ -51,6 +51,36 @@ NS_ASSUME_NONNULL_BEGIN return self; } + ++ (TSAttachmentPointer *)attachmentPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto + relay:(NSString *_Nullable)relay +{ + OWSAssert(attachmentProto.id != 0); + OWSAssert(attachmentProto.key != nil); + OWSAssert(attachmentProto.contentType != nil); + + // digest will be empty for old clients. + NSData *digest = attachmentProto.hasDigest ? attachmentProto.digest : nil; + + TSAttachmentType attachmentType = TSAttachmentTypeDefault; + if ([attachmentProto hasFlags]) { + UInt32 flags = attachmentProto.flags; + if ((flags & (UInt32)OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage) > 0) { + attachmentType = TSAttachmentTypeVoiceMessage; + } + } + + TSAttachmentPointer *pointer = [[TSAttachmentPointer alloc] initWithServerId:attachmentProto.id + key:attachmentProto.key + digest:digest + byteCount:attachmentProto.size + contentType:attachmentProto.contentType + relay:relay + sourceFilename:attachmentProto.fileName + attachmentType:attachmentType]; + return pointer; +} + - (BOOL)isDecimalNumberText:(NSString *)text { return [text componentsSeparatedByCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]].count == 1; diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h index 2bc67484d..ca97207ae 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h @@ -1,12 +1,13 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN -@class OWSSignalServiceProtosSyncMessageSent; -@class OWSSignalServiceProtosDataMessage; @class OWSSignalServiceProtosAttachmentPointer; +@class OWSSignalServiceProtosDataMessage; +@class OWSSignalServiceProtosSyncMessageSent; +@class TSQuotedMessage; @class TSThread; @class YapDatabaseReadWriteTransaction; @@ -16,7 +17,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OWSIncomingSentMessageTranscript : NSObject -- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay; +- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto + relay:(nullable NSString *)relay + transaction:(YapDatabaseReadWriteTransaction *)transaction; @property (nonatomic, readonly) NSString *relay; @property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage; @@ -30,8 +33,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable, nonatomic, readonly) NSData *groupId; @property (nonatomic, readonly) NSString *body; @property (nonatomic, readonly) NSArray *attachmentPointerProtos; - -- (TSThread *)threadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; +@property (nonatomic, readonly) TSThread *thread; +@property (nonatomic, readonly) TSQuotedMessage *quotedMessage; @end diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m index 34a65da35..ce97eefd1 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m @@ -10,13 +10,16 @@ #import "TSGroupModel.h" #import "TSGroupThread.h" #import "TSOutgoingMessage.h" +#import "TSQuotedMessage.h" #import "TSThread.h" NS_ASSUME_NONNULL_BEGIN @implementation OWSIncomingSentMessageTranscript -- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay +- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto + relay:(nullable NSString *)relay + transaction:(YapDatabaseReadWriteTransaction *)transaction { self = [super init]; if (!self) { @@ -35,6 +38,15 @@ NS_ASSUME_NONNULL_BEGIN _isExpirationTimerUpdate = (_dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0; _isEndSessionMessage = (_dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0; + if (self.dataMessage.hasGroup) { + _thread = [TSGroupThread getOrCreateThreadWithGroupId:_dataMessage.group.id transaction:transaction]; + } else { + _thread = [TSContactThread getOrCreateThreadWithContactId:_recipientId transaction:transaction]; + } + + _quotedMessage = + [TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread relay:relay transaction:transaction]; + return self; } @@ -47,15 +59,6 @@ NS_ASSUME_NONNULL_BEGIN } } -- (TSThread *)threadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - if (self.dataMessage.hasGroup) { - return [TSGroupThread getOrCreateThreadWithGroupId:self.dataMessage.group.id transaction:transaction]; - } else { - return [TSContactThread getOrCreateThreadWithContactId:self.recipientId transaction:transaction]; - } -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h index 1a51727db..e9242fe68 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.h @@ -7,9 +7,13 @@ NS_ASSUME_NONNULL_BEGIN +@class OWSSignalServiceProtosDataMessage; +@class OWSSignalServiceProtosEnvelope; @class TSAttachment; @class TSAttachmentStream; @class TSQuotedMessage; +@class TSThread; +@class YapDatabaseReadWriteTransaction; // View model which has already fetched any attachments. @interface OWSQuotedReplyModel : NSObject @@ -121,6 +125,12 @@ NS_ASSUME_NONNULL_BEGIN body:(NSString *_Nullable)body quotedAttachmentsForSending:(NSArray *)attachments; + ++ (nullable instancetype)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage + thread:(TSThread *)thread + relay:(nullable NSString *)relay + transaction:(YapDatabaseReadWriteTransaction *)transaction; + @end #pragma mark - diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m index 227a8a0c3..d09ee2447 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m @@ -3,8 +3,15 @@ // #import "TSQuotedMessage.h" +#import "OWSSignalServiceProtos.pb.h" +#import "TSAccountManager.h" #import "TSAttachment.h" +#import "TSAttachmentPointer.h" #import "TSAttachmentStream.h" +#import "TSIncomingMessage.h" +#import "TSInteraction.h" +#import "TSOutgoingMessage.h" +#import "TSThread.h" #import NS_ASSUME_NONNULL_BEGIN @@ -178,6 +185,147 @@ NS_ASSUME_NONNULL_BEGIN return self; } ++ (TSQuotedMessage *_Nullable)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage + thread:(TSThread *)thread + relay:(nullable NSString *)relay + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + OWSAssert(dataMessage); + + if (!dataMessage.hasQuote) { + return nil; + } + + OWSSignalServiceProtosDataMessageQuote *quoteProto = [dataMessage quote]; + + if (![quoteProto hasId] || [quoteProto id] == 0) { + OWSFail(@"%@ quoted message missing id", self.logTag); + return nil; + } + uint64_t timestamp = [quoteProto id]; + + if (![quoteProto hasAuthor] || [quoteProto author].length == 0) { + OWSFail(@"%@ quoted message missing author", self.logTag); + return nil; + } + // TODO: We could verify that this is a valid e164 value. + NSString *authorId = [quoteProto author]; + + NSString *_Nullable body = nil; + BOOL hasText = NO; + BOOL hasAttachment = NO; + if ([quoteProto hasText] && [quoteProto text].length > 0) { + body = [quoteProto text]; + hasText = YES; + } + + NSMutableArray *attachmentInfos = [NSMutableArray new]; + for (OWSSignalServiceProtosDataMessageQuoteQuotedAttachment *quotedAttachment in quoteProto.attachments) { + hasAttachment = YES; + OWSAttachmentInfo *attachmentInfo = [[OWSAttachmentInfo alloc] initWithAttachmentId:nil + contentType:quotedAttachment.contentType + sourceFilename:quotedAttachment.fileName]; + + // We prefer deriving any thumbnail locally rather than fetching one from the network. + TSAttachmentStream *_Nullable thumbnailStream = + [self tryToDeriveLocalThumbnailWithAttachmentInfo:attachmentInfo + timestamp:timestamp + threadId:thread.uniqueId + authorId:authorId + transaction:transaction]; + + if (thumbnailStream) { + DDLogDebug(@"%@ Generated local thumbnail for quoted quoted message: %@:%tu", + self.logTag, + thread.uniqueId, + timestamp); + + [thumbnailStream saveWithTransaction:transaction]; + + attachmentInfo.thumbnailAttachmentStreamId = thumbnailStream.uniqueId; + } else if (quotedAttachment.hasThumbnail) { + DDLogDebug(@"%@ Saving reference for fetching remote thumbnail for quoted message: %@:%tu", + self.logTag, + thread.uniqueId, + timestamp); + + OWSSignalServiceProtosAttachmentPointer *thumbnailAttachmentProto = quotedAttachment.thumbnail; + TSAttachmentPointer *thumbnailPointer = + [TSAttachmentPointer attachmentPointerFromProto:thumbnailAttachmentProto relay:relay]; + [thumbnailPointer saveWithTransaction:transaction]; + + attachmentInfo.thumbnailAttachmentPointerId = thumbnailPointer.uniqueId; + } else { + DDLogDebug(@"%@ No thumbnail for quoted message: %@:%tu", self.logTag, thread.uniqueId, timestamp); + } + + [attachmentInfos addObject:attachmentInfo]; + } + + if (!hasText && !hasAttachment) { + OWSFail(@"%@ quoted message has neither text nor attachment", self.logTag); + return nil; + } + + return [[TSQuotedMessage alloc] initWithTimestamp:timestamp + authorId:authorId + body:body + quotedAttachmentInfos:attachmentInfos]; +} + ++ (nullable TSAttachmentStream *)tryToDeriveLocalThumbnailWithAttachmentInfo:(OWSAttachmentInfo *)attachmentInfo + timestamp:(uint64_t)timestamp + threadId:(NSString *)threadId + authorId:(NSString *)authorId + transaction: + (YapDatabaseReadWriteTransaction *)transaction +{ + if (![TSAttachmentStream hasThumbnailForMimeType:attachmentInfo.contentType]) { + return nil; + } + + NSArray *quotedMessages = (NSArray *)[TSInteraction + interactionsWithTimestamp:timestamp + filter:^BOOL(TSInteraction *interaction) { + + if (![threadId isEqual:interaction.uniqueThreadId]) { + return NO; + } + + if ([interaction isKindOfClass:[TSIncomingMessage class]]) { + TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction; + return [authorId isEqual:incomingMessage.messageAuthorId]; + } else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { + return [authorId isEqual:[TSAccountManager localNumber]]; + } else { + // ignore other interaction types + return NO; + } + + } + withTransaction:transaction]; + + TSMessage *_Nullable quotedMessage = quotedMessages.firstObject; + + if (!quotedMessage) { + return nil; + } + + TSAttachment *attachment = [quotedMessage attachmentWithTransaction:transaction]; + if (![attachment isKindOfClass:[TSAttachmentStream class]]) { + return nil; + } + TSAttachmentStream *sourceStream = (TSAttachmentStream *)attachment; + + TSAttachmentStream *_Nullable thumbnailStream = [sourceStream cloneAsThumbnail]; + if (!thumbnailStream) { + return nil; + } + + return thumbnailStream; +} + + #pragma mark - Attachment (not necessarily with a thumbnail) - (nullable OWSAttachmentInfo *)firstAttachmentInfo diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 274ec66ff..999eacbe2 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -603,7 +603,9 @@ NS_ASSUME_NONNULL_BEGIN if (syncMessage.hasSent) { OWSIncomingSentMessageTranscript *transcript = - [[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:envelope.relay]; + [[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent + relay:envelope.relay + transaction:transaction]; OWSRecordTranscriptJob *recordJob = [[OWSRecordTranscriptJob alloc] initWithIncomingSentMessageTranscript:transcript]; @@ -989,10 +991,10 @@ NS_ASSUME_NONNULL_BEGIN return nil; } - TSQuotedMessage *_Nullable quotedMessage = [self quotedMessageForDataMessage:dataMessage - envelope:envelope - thread:oldGroupThread - transaction:transaction]; + TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage + thread:oldGroupThread + relay:envelope.relay + transaction:transaction]; DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu", self.logTag, @@ -1038,8 +1040,10 @@ NS_ASSUME_NONNULL_BEGIN transaction:transaction relay:envelope.relay]; - TSQuotedMessage *_Nullable quotedMessage = - [self quotedMessageForDataMessage:dataMessage envelope:envelope thread:thread transaction:transaction]; + TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage + thread:thread + relay:envelope.relay + transaction:transaction]; TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp @@ -1059,147 +1063,6 @@ NS_ASSUME_NONNULL_BEGIN } } -- (TSQuotedMessage *_Nullable)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - envelope:(OWSSignalServiceProtosEnvelope *)envelope - thread:(TSThread *)thread - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(dataMessage); - - if (!dataMessage.hasQuote) { - return nil; - } - - OWSSignalServiceProtosDataMessageQuote *quoteProto = [dataMessage quote]; - - if (![quoteProto hasId] || [quoteProto id] == 0) { - OWSFail(@"%@ quoted message missing id", self.logTag); - return nil; - } - uint64_t timestamp = [quoteProto id]; - - if (![quoteProto hasAuthor] || [quoteProto author].length == 0) { - OWSFail(@"%@ quoted message missing author", self.logTag); - return nil; - } - // TODO: We could verify that this is a valid e164 value. - NSString *authorId = [quoteProto author]; - - NSString *_Nullable body = nil; - BOOL hasText = NO; - BOOL hasAttachment = NO; - if ([quoteProto hasText] && [quoteProto text].length > 0) { - body = [quoteProto text]; - hasText = YES; - } - - NSMutableArray *attachmentInfos = [NSMutableArray new]; - for (OWSSignalServiceProtosDataMessageQuoteQuotedAttachment *quotedAttachment in quoteProto.attachments) { - hasAttachment = YES; - OWSAttachmentInfo *attachmentInfo = - [[OWSAttachmentInfo alloc] initWithAttachmentId:nil - contentType:quotedAttachment.contentType - sourceFilename:quotedAttachment.fileName]; - - // We prefer deriving any thumbnail locally rather than fetching one from the network. - TSAttachmentStream *_Nullable thumbnailStream = - [self tryToDeriveLocalThumbnailWithAttachmentInfo:attachmentInfo - timestamp:timestamp - threadId:thread.uniqueId - authorId:authorId - transaction:transaction]; - - if (thumbnailStream) { - DDLogDebug(@"%@ Generated local thumbnail for quoted quoted message: %@:%zu", - self.logTag, - thread.uniqueId, - timestamp); - - [thumbnailStream saveWithTransaction:transaction]; - - attachmentInfo.thumbnailAttachmentStreamId = thumbnailStream.uniqueId; - } else if (quotedAttachment.hasThumbnail) { - DDLogDebug(@"%@ Saving reference for fetching remote thumbnail for quoted message: %@:%zu", - self.logTag, - thread.uniqueId, - timestamp); - - OWSSignalServiceProtosAttachmentPointer *thumbnailAttachmentProto = quotedAttachment.thumbnail; - TSAttachmentPointer *thumbnailPointer = - [OWSAttachmentsProcessor buildPointerFromProto:thumbnailAttachmentProto relay:envelope.relay]; - [thumbnailPointer saveWithTransaction:transaction]; - - attachmentInfo.thumbnailAttachmentPointerId = thumbnailPointer.uniqueId; - } else { - DDLogDebug(@"%@ No thumbnail for quoted message: %@:%zu", self.logTag, thread.uniqueId, timestamp); - } - - [attachmentInfos addObject:attachmentInfo]; - } - - if (!hasText && !hasAttachment) { - OWSFail(@"%@ quoted message has neither text nor attachment", self.logTag); - return nil; - } - - return [[TSQuotedMessage alloc] initWithTimestamp:timestamp - authorId:authorId - body:body - quotedAttachmentInfos:attachmentInfos]; -} - -- (nullable TSAttachmentStream *)tryToDeriveLocalThumbnailWithAttachmentInfo:(OWSAttachmentInfo *)attachmentInfo - timestamp:(uint64_t)timestamp - threadId:(NSString *)threadId - authorId:(NSString *)authorId - transaction: - (YapDatabaseReadWriteTransaction *)transaction -{ - if (![TSAttachmentStream hasThumbnailForMimeType:attachmentInfo.contentType]) { - return nil; - } - - NSArray *quotedMessages = (NSArray *)[TSInteraction - interactionsWithTimestamp:timestamp - filter:^BOOL(TSInteraction *interaction) { - - if (![threadId isEqual:interaction.uniqueThreadId]) { - return NO; - } - - if ([interaction isKindOfClass:[TSIncomingMessage class]]) { - TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction; - return [authorId isEqual:incomingMessage.messageAuthorId]; - } else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { - return [authorId isEqual:[TSAccountManager localNumber]]; - } else { - // ignore other interaction types - return NO; - } - - } - withTransaction:transaction]; - - TSMessage *_Nullable quotedMessage = quotedMessages.firstObject; - - if (!quotedMessage) { - return nil; - } - - TSAttachment *attachment = [quotedMessage attachmentWithTransaction:transaction]; - if (![attachment isKindOfClass:[TSAttachmentStream class]]) { - return nil; - } - TSAttachmentStream *sourceStream = (TSAttachmentStream *)attachment; - - TSAttachmentStream *_Nullable thumbnailStream = [sourceStream cloneAsThumbnail]; - if (!thumbnailStream) { - return nil; - } - - return thumbnailStream; -} - - (void)finalizeIncomingMessage:(TSIncomingMessage *)incomingMessage thread:(TSThread *)thread envelope:(OWSSignalServiceProtosEnvelope *)envelope