Do not confirm messages until we have handled them

This commit is contained in:
Josh Perez 2023-08-21 16:08:27 -04:00 committed by GitHub
parent 29aa188c0f
commit 04f716986c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 990 additions and 960 deletions

View file

@ -1,112 +1,115 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable max-classes-per-file */
import { Collection, Model } from 'backbone';
import type { ConversationModel } from '../models/conversations';
import * as log from '../logging/log';
import * as Errors from '../types/errors';
import type { AciString } from '../types/ServiceId';
import type { ConversationModel } from '../models/conversations';
import * as Errors from '../types/errors';
import * as log from '../logging/log';
import { drop } from '../util/drop';
import { getConversationIdForLogging } from '../util/idForLogging';
export type MessageRequestAttributesType = {
threadE164?: string;
threadAci?: AciString;
envelopeId: string;
groupV2Id?: string;
removeFromMessageReceiverCache: () => unknown;
threadAci?: AciString;
threadE164?: string;
type: number;
};
class MessageRequestModel extends Model<MessageRequestAttributesType> {}
const messageRequests = new Map<string, MessageRequestAttributesType>();
let singleton: MessageRequests | undefined;
function remove(sync: MessageRequestAttributesType): void {
messageRequests.delete(sync.envelopeId);
sync.removeFromMessageReceiverCache();
}
export class MessageRequests extends Collection<MessageRequestModel> {
static getSingleton(): MessageRequests {
if (!singleton) {
singleton = new MessageRequests();
export function forConversation(
conversation: ConversationModel
): MessageRequestAttributesType | null {
const logId = `MessageRequests.forConversation(${getConversationIdForLogging(
conversation.attributes
)})`;
const messageRequestValues = Array.from(messageRequests.values());
if (conversation.get('e164')) {
const syncByE164 = messageRequestValues.find(
item => item.threadE164 === conversation.get('e164')
);
if (syncByE164) {
log.info(`${logId}: Found early message request response for E164`);
remove(syncByE164);
return syncByE164;
}
return singleton;
}
forConversation(conversation: ConversationModel): MessageRequestModel | null {
if (conversation.get('e164')) {
const syncByE164 = this.findWhere({
threadE164: conversation.get('e164'),
});
if (syncByE164) {
log.info(
`Found early message request response for E164 ${conversation.idForLogging()}`
);
this.remove(syncByE164);
return syncByE164;
}
if (conversation.getServiceId()) {
const syncByServiceId = messageRequestValues.find(
item => item.threadAci === conversation.getServiceId()
);
if (syncByServiceId) {
log.info(`${logId}: Found early message request response for serviceId`);
remove(syncByServiceId);
return syncByServiceId;
}
if (conversation.getServiceId()) {
const syncByAci = this.findWhere({
threadAci: conversation.getServiceId(),
});
if (syncByAci) {
log.info(
`Found early message request response for aci ${conversation.idForLogging()}`
);
this.remove(syncByAci);
return syncByAci;
}
}
// V2 group
if (conversation.get('groupId')) {
const syncByGroupId = this.findWhere({
groupV2Id: conversation.get('groupId'),
});
if (syncByGroupId) {
log.info(
`Found early message request response for group v2 ID ${conversation.idForLogging()}`
);
this.remove(syncByGroupId);
return syncByGroupId;
}
}
return null;
}
async onResponse(sync: MessageRequestModel): Promise<void> {
try {
const threadE164 = sync.get('threadE164');
const threadAci = sync.get('threadAci');
const groupV2Id = sync.get('groupV2Id');
// V2 group
if (conversation.get('groupId')) {
const syncByGroupId = messageRequestValues.find(
item => item.groupV2Id === conversation.get('groupId')
);
if (syncByGroupId) {
log.info(`${logId}: Found early message request response for gv2`);
remove(syncByGroupId);
return syncByGroupId;
}
}
let conversation;
return null;
}
// We multiplex between GV1/GV2 groups here, but we don't kick off migrations
if (groupV2Id) {
conversation = window.ConversationController.get(groupV2Id);
}
if (!conversation && (threadE164 || threadAci)) {
conversation = window.ConversationController.lookupOrCreate({
e164: threadE164,
serviceId: threadAci,
reason: 'MessageRequests.onResponse',
});
}
export async function onResponse(
sync: MessageRequestAttributesType
): Promise<void> {
messageRequests.set(sync.envelopeId, sync);
const { threadE164, threadAci, groupV2Id } = sync;
if (!conversation) {
log.warn(
`Received message request response for unknown conversation: groupv2(${groupV2Id}) ${threadAci} ${threadE164}`
);
return;
}
const logId = `MessageRequests.onResponse(groupv2(${groupV2Id}) ${threadAci} ${threadE164})`;
void conversation.applyMessageRequestResponse(sync.get('type'), {
try {
let conversation;
// We multiplex between GV1/GV2 groups here, but we don't kick off migrations
if (groupV2Id) {
conversation = window.ConversationController.get(groupV2Id);
}
if (!conversation && (threadE164 || threadAci)) {
conversation = window.ConversationController.lookupOrCreate({
e164: threadE164,
serviceId: threadAci,
reason: logId,
});
}
if (!conversation) {
log.warn(
`${logId}: received message request response for unknown conversation`
);
remove(sync);
return;
}
drop(
conversation.applyMessageRequestResponse(sync.type, {
fromSync: true,
});
})
);
this.remove(sync);
} catch (error) {
log.error('MessageRequests.onResponse error:', Errors.toLogFormat(error));
}
remove(sync);
} catch (error) {
remove(sync);
log.error(`${logId} error:`, Errors.toLogFormat(error));
}
}