Quotes: Check message's conversationId

This commit is contained in:
Scott Nonnenberg 2021-04-14 15:15:57 -07:00 committed by GitHub
parent 97a4361c6f
commit 7d55421d8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 58 deletions

24
ts/model-types.d.ts vendored
View file

@ -52,6 +52,18 @@ export type GroupMigrationType = {
invitedMembers: Array<GroupV2PendingMemberType>; invitedMembers: Array<GroupV2PendingMemberType>;
}; };
export type QuotedMessageType = {
attachments: Array<typeof window.WhatIsThis>;
// `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 = { export type MessageAttributesType = {
bodyPending: boolean; bodyPending: boolean;
bodyRanges: BodyRangesType; bodyRanges: BodyRangesType;
@ -86,17 +98,7 @@ export type MessageAttributesType = {
message: unknown; message: unknown;
messageTimer: unknown; messageTimer: unknown;
profileChange: ProfileNameChangeType; profileChange: ProfileNameChangeType;
quote?: { quote?: QuotedMessageType;
attachments: Array<typeof window.WhatIsThis>;
// `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;
};
reactions?: Array<{ reactions?: Array<{
emoji: string; emoji: string;
timestamp: number; timestamp: number;

View file

@ -2,9 +2,10 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { import {
WhatIsThis,
MessageAttributesType,
CustomError, CustomError,
MessageAttributesType,
QuotedMessageType,
WhatIsThis,
} from '../model-types.d'; } from '../model-types.d';
import { DataMessageClass } from '../textsecure.d'; import { DataMessageClass } from '../textsecure.d';
import { ConversationModel } from './conversations'; import { ConversationModel } from './conversations';
@ -44,6 +45,7 @@ import {
} from '../util/callingNotification'; } from '../util/callingNotification';
import { PropsType as ProfileChangeNotificationPropsType } from '../components/conversation/ProfileChangeNotification'; import { PropsType as ProfileChangeNotificationPropsType } from '../components/conversation/ProfileChangeNotification';
import { AttachmentType, isImage, isVideo } from '../types/Attachment'; import { AttachmentType, isImage, isVideo } from '../types/Attachment';
import { MIMEType } from '../types/MIME';
/* eslint-disable camelcase */ /* eslint-disable camelcase */
/* eslint-disable more/no-then */ /* eslint-disable more/no-then */
@ -1114,10 +1116,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
) )
: undefined; : undefined;
let reallyNotFound = referencedMessageNotFound; let foundReference = !referencedMessageNotFound;
// Is the quote really without a reference? Check with our in memory store // Is the quote really without a reference? Check with our in memory store
// first to make sure it's not there. // first to make sure it's not there.
if (referencedMessageNotFound) { if (referencedMessageNotFound && contact) {
const messageId = this.get('sent_at'); const messageId = this.get('sent_at');
window.log.info( window.log.info(
`getPropsForQuote: Verifying that ${messageId} referencing ${sentAt} is really not found` `getPropsForQuote: Verifying that ${messageId} referencing ${sentAt} is really not found`
@ -1125,11 +1127,20 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const inMemoryMessage = window.MessageController.findBySentAt( const inMemoryMessage = window.MessageController.findBySentAt(
Number(sentAt) Number(sentAt)
); );
reallyNotFound = !inMemoryMessage; if (
this.isQuoteAMatch(inMemoryMessage, this.get('conversationId'), quote)
) {
foundReference = true;
this.set({
quote: {
...quote,
referencedMessageNotFound: false,
},
});
const fetchDataAndUpdate = async () => {
await this.copyQuoteContentFromOriginal(inMemoryMessage, quote);
// 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( window.log.info(
`getPropsForQuote: Found ${sentAt}, scheduling an update to ${messageId}` `getPropsForQuote: Found ${sentAt}, scheduling an update to ${messageId}`
); );
@ -1140,6 +1151,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}, },
}); });
window.Signal.Util.queueUpdateMessage(this.attributes); window.Signal.Util.queueUpdateMessage(this.attributes);
};
fetchDataAndUpdate();
} }
} }
@ -1190,7 +1203,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
rawAttachment: firstAttachment rawAttachment: firstAttachment
? this.processQuoteAttachment(firstAttachment) ? this.processQuoteAttachment(firstAttachment)
: undefined, : undefined,
referencedMessageNotFound: reallyNotFound, referencedMessageNotFound: !foundReference,
sentAt: Number(sentAt), sentAt: Number(sentAt),
text: this.createNonBreakingLastSeparator(text), text: this.createNonBreakingLastSeparator(text),
}; };
@ -2999,37 +3012,30 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
async copyFromQuotedMessage(message: WhatIsThis): Promise<boolean> { async copyFromQuotedMessage(
message: DataMessageClass,
conversationId: string
): Promise<DataMessageClass> {
const { quote } = message; const { quote } = message;
if (!quote) { if (!quote) {
return message; return message;
} }
const { attachments, id, author, authorUuid } = quote; const { id } = quote;
const firstAttachment = attachments[0];
const authorConversationId = window.ConversationController.ensureContactIds(
{
e164: author,
uuid: authorUuid,
}
);
const inMemoryMessage = window.MessageController.findBySentAt(id); const inMemoryMessage = window.MessageController.findBySentAt(id);
let queryMessage; let queryMessage;
if (inMemoryMessage) { if (this.isQuoteAMatch(inMemoryMessage, conversationId, quote)) {
queryMessage = inMemoryMessage; queryMessage = inMemoryMessage;
} else { } else {
window.log.info('copyFromQuotedMessage: db lookup needed', id); window.log.info('copyFromQuotedMessage: db lookup needed', id);
const collection = await window.Signal.Data.getMessagesBySentAt(id, { const collection = await window.Signal.Data.getMessagesBySentAt(id, {
MessageCollection: window.Whisper.MessageCollection, MessageCollection: window.Whisper.MessageCollection,
}); });
const found = collection.find(item => { const found = collection.find(item =>
const messageAuthorId = item.getContactId(); this.isQuoteAMatch(item, conversationId, quote)
);
return authorConversationId === messageAuthorId;
});
if (!found) { if (!found) {
quote.referencedMessageNotFound = true; quote.referencedMessageNotFound = true;
@ -3039,39 +3045,82 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
queryMessage = window.MessageController.register(found.id, found); 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<void> {
const { attachments } = quote;
const firstAttachment = attachments ? attachments[0] : undefined;
if (originalMessage.isTapToView()) {
// eslint-disable-next-line no-param-reassign
quote.text = null; quote.text = null;
// eslint-disable-next-line no-param-reassign
quote.attachments = [ quote.attachments = [
{ {
contentType: 'image/jpeg', 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) { if (firstAttachment) {
firstAttachment.thumbnail = null; firstAttachment.thumbnail = undefined;
} }
if ( if (
!firstAttachment || !firstAttachment ||
(!GoogleChrome.isImageTypeSupported(firstAttachment.contentType) && !firstAttachment.contentType ||
!GoogleChrome.isVideoTypeSupported(firstAttachment.contentType)) (!GoogleChrome.isImageTypeSupported(
firstAttachment.contentType as MIMEType
) &&
!GoogleChrome.isVideoTypeSupported(
firstAttachment.contentType as MIMEType
))
) { ) {
return message; return;
} }
try { try {
if ( if (
queryMessage.get('schemaVersion') < originalMessage.get('schemaVersion') <
TypedMessage.VERSION_NEEDED_FOR_DISPLAY TypedMessage.VERSION_NEEDED_FOR_DISPLAY
) { ) {
const upgradedMessage = await upgradeMessageSchema( const upgradedMessage = await upgradeMessageSchema(
queryMessage.attributes originalMessage.attributes
); );
queryMessage.set(upgradedMessage); originalMessage.set(upgradedMessage);
await window.Signal.Data.saveMessage(upgradedMessage, { await window.Signal.Data.saveMessage(upgradedMessage, {
Message: window.Whisper.Message, Message: window.Whisper.Message,
}); });
@ -3081,10 +3130,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
'Problem upgrading message quoted message from database', 'Problem upgrading message quoted message from database',
Errors.toLogFormat(error) Errors.toLogFormat(error)
); );
return message; return;
} }
const queryAttachments = queryMessage.get('attachments') || []; const queryAttachments = originalMessage.get('attachments') || [];
if (queryAttachments.length > 0) { if (queryAttachments.length > 0) {
const queryFirst = queryAttachments[0]; const queryFirst = queryAttachments[0];
const { thumbnail } = queryFirst; const { thumbnail } = queryFirst;
@ -3097,7 +3146,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
} }
const queryPreview = queryMessage.get('preview') || []; const queryPreview = originalMessage.get('preview') || [];
if (queryPreview.length > 0) { if (queryPreview.length > 0) {
const queryFirst = queryPreview[0]; const queryFirst = queryPreview[0];
const { image } = queryFirst; const { image } = queryFirst;
@ -3110,15 +3159,13 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
} }
const sticker = queryMessage.get('sticker'); const sticker = originalMessage.get('sticker');
if (sticker && sticker.data && sticker.data.path) { if (sticker && sticker.data && sticker.data.path) {
firstAttachment.thumbnail = { firstAttachment.thumbnail = {
...sticker.data, ...sticker.data,
copied: true, copied: true,
}; };
} }
return message;
} }
handleDataMessage( handleDataMessage(
@ -3366,7 +3413,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
const withQuoteReference = await this.copyFromQuotedMessage( const withQuoteReference = await this.copyFromQuotedMessage(
initialMessage initialMessage,
conversation.id
); );
const dataMessage = await upgradeMessageSchema(withQuoteReference); const dataMessage = await upgradeMessageSchema(withQuoteReference);

3
ts/textsecure.d.ts vendored
View file

@ -725,6 +725,9 @@ export declare namespace DataMessageClass {
text: string | null; text: string | null;
attachments?: Array<DataMessageClass.Quote.QuotedAttachment>; attachments?: Array<DataMessageClass.Quote.QuotedAttachment>;
bodyRanges?: Array<DataMessageClass.BodyRange>; bodyRanges?: Array<DataMessageClass.BodyRange>;
// Added later during processing
referencedMessageNotFound?: boolean;
} }
class BodyRange { class BodyRange {