2022-08-16 23:59:11 +00:00
|
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import type { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
|
|
|
import * as log from '../logging/log';
|
|
|
|
import { explodePromise } from './explodePromise';
|
2022-11-11 04:10:30 +00:00
|
|
|
import type { RecipientsByConversation } from '../state/ducks/stories';
|
|
|
|
import { isNotNil } from './isNotNil';
|
|
|
|
import type { UUIDStringType } from '../types/UUID';
|
|
|
|
import { waitForAll } from './waitForAll';
|
2022-08-16 23:59:11 +00:00
|
|
|
|
|
|
|
export async function blockSendUntilConversationsAreVerified(
|
2022-11-11 04:10:30 +00:00
|
|
|
byConversationId: RecipientsByConversation,
|
|
|
|
source: SafetyNumberChangeSource,
|
2022-08-19 18:05:31 +00:00
|
|
|
timestampThreshold?: number
|
2022-08-16 23:59:11 +00:00
|
|
|
): Promise<boolean> {
|
2022-11-11 04:10:30 +00:00
|
|
|
const allUuids = getAllUuids(byConversationId);
|
|
|
|
await waitForAll({
|
|
|
|
tasks: Array.from(allUuids).map(uuid => async () => updateUuidTrust(uuid)),
|
|
|
|
});
|
2022-08-16 23:59:11 +00:00
|
|
|
|
2022-11-11 04:10:30 +00:00
|
|
|
const untrustedByConversation = filterUuids(
|
|
|
|
byConversationId,
|
|
|
|
(uuid: UUIDStringType) => !isUuidTrusted(uuid, timestampThreshold)
|
2022-08-16 23:59:11 +00:00
|
|
|
);
|
|
|
|
|
2022-11-11 04:10:30 +00:00
|
|
|
const untrustedUuids = getAllUuids(untrustedByConversation);
|
|
|
|
if (untrustedUuids.size) {
|
|
|
|
log.info(
|
|
|
|
`blockSendUntilConversationsAreVerified: Blocking send; ${untrustedUuids.size} untrusted uuids`
|
|
|
|
);
|
|
|
|
|
2022-08-16 23:59:11 +00:00
|
|
|
const explodedPromise = explodePromise<boolean>();
|
|
|
|
window.reduxActions.globalModals.showBlockingSafetyNumberChangeDialog(
|
2022-11-11 04:10:30 +00:00
|
|
|
untrustedByConversation,
|
2022-08-16 23:59:11 +00:00
|
|
|
explodedPromise,
|
|
|
|
source
|
|
|
|
);
|
|
|
|
return explodedPromise.promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2022-11-11 04:10:30 +00:00
|
|
|
|
|
|
|
async function updateUuidTrust(uuid: string) {
|
|
|
|
const conversation = window.ConversationController.get(uuid);
|
|
|
|
if (!conversation) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
await conversation.updateVerified();
|
|
|
|
}
|
|
|
|
|
|
|
|
function isUuidTrusted(uuid: string, timestampThreshold?: number) {
|
|
|
|
const conversation = window.ConversationController.get(uuid);
|
|
|
|
if (!conversation) {
|
|
|
|
log.warn(
|
|
|
|
`blockSendUntilConversationsAreVerified/isUuidTrusted: No conversation for send target ${uuid}`
|
|
|
|
);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const unverifieds = conversation.getUnverified();
|
|
|
|
if (unverifieds.length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const untrusted = conversation.getUntrusted(timestampThreshold);
|
|
|
|
if (untrusted.length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getAllUuids(
|
|
|
|
byConversation: RecipientsByConversation
|
|
|
|
): Set<UUIDStringType> {
|
|
|
|
const allUuids = new Set<UUIDStringType>();
|
|
|
|
Object.values(byConversation).forEach(conversationData => {
|
|
|
|
conversationData.uuids.forEach(uuid => allUuids.add(uuid));
|
|
|
|
|
|
|
|
if (conversationData.byDistributionId) {
|
|
|
|
Object.values(conversationData.byDistributionId).forEach(
|
|
|
|
distributionData => {
|
|
|
|
distributionData.uuids.forEach(uuid => allUuids.add(uuid));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return allUuids;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function filterUuids(
|
|
|
|
byConversation: RecipientsByConversation,
|
|
|
|
predicate: (uuid: UUIDStringType) => boolean
|
|
|
|
): RecipientsByConversation {
|
|
|
|
const filteredByConversation: RecipientsByConversation = {};
|
|
|
|
Object.entries(byConversation).forEach(
|
|
|
|
([conversationId, conversationData]) => {
|
|
|
|
const conversationFiltered = conversationData.uuids
|
|
|
|
.map(uuid => {
|
|
|
|
if (predicate(uuid)) {
|
|
|
|
return uuid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
})
|
|
|
|
.filter(isNotNil);
|
|
|
|
|
|
|
|
let byDistributionId:
|
|
|
|
| Record<string, { uuids: Array<UUIDStringType> }>
|
|
|
|
| undefined;
|
|
|
|
|
|
|
|
if (conversationData.byDistributionId) {
|
|
|
|
Object.entries(conversationData.byDistributionId).forEach(
|
|
|
|
([distributionId, distributionData]) => {
|
|
|
|
const distributionFiltered = distributionData.uuids
|
|
|
|
.map(uuid => {
|
|
|
|
if (predicate(uuid)) {
|
|
|
|
return uuid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
})
|
|
|
|
.filter(isNotNil);
|
|
|
|
|
|
|
|
if (distributionFiltered.length) {
|
|
|
|
byDistributionId = {
|
|
|
|
...byDistributionId,
|
|
|
|
[distributionId]: {
|
|
|
|
uuids: distributionFiltered,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conversationFiltered.length || byDistributionId) {
|
|
|
|
filteredByConversation[conversationId] = {
|
|
|
|
uuids: conversationFiltered,
|
|
|
|
...(byDistributionId ? { byDistributionId } : undefined),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return filteredByConversation;
|
|
|
|
}
|