From 7d55421d8f92555f385ef8a07fa3266d57a2ef60 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Wed, 14 Apr 2021 15:15:57 -0700 Subject: [PATCH] Quotes: Check message's conversationId --- ts/model-types.d.ts | 24 +++---- ts/models/messages.ts | 142 ++++++++++++++++++++++++++++-------------- ts/textsecure.d.ts | 3 + 3 files changed, 111 insertions(+), 58 deletions(-) diff --git a/ts/model-types.d.ts b/ts/model-types.d.ts index f91e04a493dc..590f200fbef2 100644 --- a/ts/model-types.d.ts +++ b/ts/model-types.d.ts @@ -52,6 +52,18 @@ export type GroupMigrationType = { invitedMembers: Array; }; +export type QuotedMessageType = { + attachments: Array; + // `author` is an old attribute that holds the author's E164. We shouldn't use it for + // new messages, but old messages might have this attribute. + author?: string; + authorUuid: string; + bodyRanges: BodyRangesType; + id: string; + referencedMessageNotFound: boolean; + text: string; +}; + export type MessageAttributesType = { bodyPending: boolean; bodyRanges: BodyRangesType; @@ -86,17 +98,7 @@ export type MessageAttributesType = { message: unknown; messageTimer: unknown; profileChange: ProfileNameChangeType; - quote?: { - attachments: Array; - // `author` is an old attribute that holds the author's E164. We shouldn't use it for - // new messages, but old messages might have this attribute. - author?: string; - authorUuid: string; - bodyRanges: BodyRangesType; - id: string; - referencedMessageNotFound: boolean; - text: string; - }; + quote?: QuotedMessageType; reactions?: Array<{ emoji: string; timestamp: number; diff --git a/ts/models/messages.ts b/ts/models/messages.ts index f6d13078218c..a49f92a7f0b9 100644 --- a/ts/models/messages.ts +++ b/ts/models/messages.ts @@ -2,9 +2,10 @@ // SPDX-License-Identifier: AGPL-3.0-only import { - WhatIsThis, - MessageAttributesType, CustomError, + MessageAttributesType, + QuotedMessageType, + WhatIsThis, } from '../model-types.d'; import { DataMessageClass } from '../textsecure.d'; import { ConversationModel } from './conversations'; @@ -44,6 +45,7 @@ import { } from '../util/callingNotification'; import { PropsType as ProfileChangeNotificationPropsType } from '../components/conversation/ProfileChangeNotification'; import { AttachmentType, isImage, isVideo } from '../types/Attachment'; +import { MIMEType } from '../types/MIME'; /* eslint-disable camelcase */ /* eslint-disable more/no-then */ @@ -1114,10 +1116,10 @@ export class MessageModel extends window.Backbone.Model { ) : undefined; - let reallyNotFound = referencedMessageNotFound; + let foundReference = !referencedMessageNotFound; // Is the quote really without a reference? Check with our in memory store // first to make sure it's not there. - if (referencedMessageNotFound) { + if (referencedMessageNotFound && contact) { const messageId = this.get('sent_at'); window.log.info( `getPropsForQuote: Verifying that ${messageId} referencing ${sentAt} is really not found` @@ -1125,21 +1127,32 @@ export class MessageModel extends window.Backbone.Model { const inMemoryMessage = window.MessageController.findBySentAt( Number(sentAt) ); - reallyNotFound = !inMemoryMessage; - - // We found the quote in memory so update the message in the database - // so we don't have to do this check again - if (!reallyNotFound) { - window.log.info( - `getPropsForQuote: Found ${sentAt}, scheduling an update to ${messageId}` - ); + if ( + this.isQuoteAMatch(inMemoryMessage, this.get('conversationId'), quote) + ) { + foundReference = true; this.set({ quote: { ...quote, referencedMessageNotFound: false, }, }); - window.Signal.Util.queueUpdateMessage(this.attributes); + + const fetchDataAndUpdate = async () => { + await this.copyQuoteContentFromOriginal(inMemoryMessage, quote); + + window.log.info( + `getPropsForQuote: Found ${sentAt}, scheduling an update to ${messageId}` + ); + this.set({ + quote: { + ...quote, + referencedMessageNotFound: false, + }, + }); + window.Signal.Util.queueUpdateMessage(this.attributes); + }; + fetchDataAndUpdate(); } } @@ -1190,7 +1203,7 @@ export class MessageModel extends window.Backbone.Model { rawAttachment: firstAttachment ? this.processQuoteAttachment(firstAttachment) : undefined, - referencedMessageNotFound: reallyNotFound, + referencedMessageNotFound: !foundReference, sentAt: Number(sentAt), text: this.createNonBreakingLastSeparator(text), }; @@ -2999,37 +3012,30 @@ export class MessageModel extends window.Backbone.Model { } // eslint-disable-next-line class-methods-use-this - async copyFromQuotedMessage(message: WhatIsThis): Promise { + async copyFromQuotedMessage( + message: DataMessageClass, + conversationId: string + ): Promise { const { quote } = message; if (!quote) { return message; } - const { attachments, id, author, authorUuid } = quote; - const firstAttachment = attachments[0]; - const authorConversationId = window.ConversationController.ensureContactIds( - { - e164: author, - uuid: authorUuid, - } - ); - + const { id } = quote; const inMemoryMessage = window.MessageController.findBySentAt(id); let queryMessage; - if (inMemoryMessage) { + if (this.isQuoteAMatch(inMemoryMessage, conversationId, quote)) { queryMessage = inMemoryMessage; } else { window.log.info('copyFromQuotedMessage: db lookup needed', id); const collection = await window.Signal.Data.getMessagesBySentAt(id, { MessageCollection: window.Whisper.MessageCollection, }); - const found = collection.find(item => { - const messageAuthorId = item.getContactId(); - - return authorConversationId === messageAuthorId; - }); + const found = collection.find(item => + this.isQuoteAMatch(item, conversationId, quote) + ); if (!found) { quote.referencedMessageNotFound = true; @@ -3039,39 +3045,82 @@ export class MessageModel extends window.Backbone.Model { queryMessage = window.MessageController.register(found.id, found); } - if (queryMessage.isTapToView()) { + await this.copyQuoteContentFromOriginal(queryMessage, quote); + + return message; + } + + // eslint-disable-next-line class-methods-use-this + isQuoteAMatch( + message: MessageModel | null | undefined, + conversationId: string, + quote: QuotedMessageType | DataMessageClass.Quote + ): message is MessageModel { + const { authorUuid, id } = quote; + const authorConversationId = window.ConversationController.ensureContactIds( + { + e164: 'author' in quote ? quote.author : undefined, + uuid: authorUuid, + } + ); + + return Boolean( + message && + message.get('sent_at') === id && + message.get('conversationId') === conversationId && + message.getContactId() === authorConversationId + ); + } + + // eslint-disable-next-line class-methods-use-this + async copyQuoteContentFromOriginal( + originalMessage: MessageModel, + quote: QuotedMessageType | DataMessageClass.Quote + ): Promise { + const { attachments } = quote; + const firstAttachment = attachments ? attachments[0] : undefined; + + if (originalMessage.isTapToView()) { + // eslint-disable-next-line no-param-reassign quote.text = null; + // eslint-disable-next-line no-param-reassign quote.attachments = [ { contentType: 'image/jpeg', }, ]; - return message; + return; } - quote.text = queryMessage.get('body'); + // eslint-disable-next-line no-param-reassign + quote.text = originalMessage.get('body'); if (firstAttachment) { - firstAttachment.thumbnail = null; + firstAttachment.thumbnail = undefined; } if ( !firstAttachment || - (!GoogleChrome.isImageTypeSupported(firstAttachment.contentType) && - !GoogleChrome.isVideoTypeSupported(firstAttachment.contentType)) + !firstAttachment.contentType || + (!GoogleChrome.isImageTypeSupported( + firstAttachment.contentType as MIMEType + ) && + !GoogleChrome.isVideoTypeSupported( + firstAttachment.contentType as MIMEType + )) ) { - return message; + return; } try { if ( - queryMessage.get('schemaVersion') < + originalMessage.get('schemaVersion') < TypedMessage.VERSION_NEEDED_FOR_DISPLAY ) { const upgradedMessage = await upgradeMessageSchema( - queryMessage.attributes + originalMessage.attributes ); - queryMessage.set(upgradedMessage); + originalMessage.set(upgradedMessage); await window.Signal.Data.saveMessage(upgradedMessage, { Message: window.Whisper.Message, }); @@ -3081,10 +3130,10 @@ export class MessageModel extends window.Backbone.Model { 'Problem upgrading message quoted message from database', Errors.toLogFormat(error) ); - return message; + return; } - const queryAttachments = queryMessage.get('attachments') || []; + const queryAttachments = originalMessage.get('attachments') || []; if (queryAttachments.length > 0) { const queryFirst = queryAttachments[0]; const { thumbnail } = queryFirst; @@ -3097,7 +3146,7 @@ export class MessageModel extends window.Backbone.Model { } } - const queryPreview = queryMessage.get('preview') || []; + const queryPreview = originalMessage.get('preview') || []; if (queryPreview.length > 0) { const queryFirst = queryPreview[0]; const { image } = queryFirst; @@ -3110,15 +3159,13 @@ export class MessageModel extends window.Backbone.Model { } } - const sticker = queryMessage.get('sticker'); + const sticker = originalMessage.get('sticker'); if (sticker && sticker.data && sticker.data.path) { firstAttachment.thumbnail = { ...sticker.data, copied: true, }; } - - return message; } handleDataMessage( @@ -3366,7 +3413,8 @@ export class MessageModel extends window.Backbone.Model { } const withQuoteReference = await this.copyFromQuotedMessage( - initialMessage + initialMessage, + conversation.id ); const dataMessage = await upgradeMessageSchema(withQuoteReference); diff --git a/ts/textsecure.d.ts b/ts/textsecure.d.ts index 97834541be1c..71c3d8f6e22f 100644 --- a/ts/textsecure.d.ts +++ b/ts/textsecure.d.ts @@ -725,6 +725,9 @@ export declare namespace DataMessageClass { text: string | null; attachments?: Array; bodyRanges?: Array; + + // Added later during processing + referencedMessageNotFound?: boolean; } class BodyRange {