fix: call messages expires with recipients expire settings

pull/2940/head
Audric Ackermann 1 year ago
parent 3a26285667
commit ccbe3e13b0

@ -1,3 +1,5 @@
import { isNil } from 'lodash';
/** /**
* This function is used to check that an optional property on a Protobuf object is not undefined or using a type-specific default value. * This function is used to check that an optional property on a Protobuf object is not undefined or using a type-specific default value.
* https://protobuf.dev/programming-guides/proto/#optional * https://protobuf.dev/programming-guides/proto/#optional
@ -10,7 +12,7 @@ function hasDefinedProperty<A extends object, B extends PropertyKey & keyof A>(
object: A, object: A,
property: B property: B
) { ) {
return Object.prototype.hasOwnProperty.call(object, property) !== false; return !isNil(object) && Object.prototype.hasOwnProperty.call(object, property) !== false;
} }
export const ProtobufUtils = { export const ProtobufUtils = {

@ -19,7 +19,7 @@ import { getConversationController } from '../session/conversations';
import { concatUInt8Array, getSodiumRenderer } from '../session/crypto'; import { concatUInt8Array, getSodiumRenderer } from '../session/crypto';
import { removeMessagePadding } from '../session/crypto/BufferPadding'; import { removeMessagePadding } from '../session/crypto/BufferPadding';
import { DisappearingMessages } from '../session/disappearing_messages'; import { DisappearingMessages } from '../session/disappearing_messages';
import { DisappearingMessageUpdate } from '../session/disappearing_messages/types'; import { ReadyToDisappearMsgUpdate } from '../session/disappearing_messages/types';
import { ProfileManager } from '../session/profile_manager/ProfileManager'; import { ProfileManager } from '../session/profile_manager/ProfileManager';
import { GroupUtils, UserUtils } from '../session/utils'; import { GroupUtils, UserUtils } from '../session/utils';
import { perfEnd, perfStart } from '../session/utils/Performance'; import { perfEnd, perfStart } from '../session/utils/Performance';
@ -547,10 +547,19 @@ export async function innerHandleSwarmContentMessage({
if (content.dataExtractionNotification) { if (content.dataExtractionNotification) {
perfStart(`handleDataExtractionNotification-${envelope.id}`); perfStart(`handleDataExtractionNotification-${envelope.id}`);
// DataExtractionNotification uses the expiration setting of our side of the 1o1 conversation. whatever we get in the contentMessage
const expirationTimer = senderConversationModel.getExpireTimer();
const expirationType = DisappearingMessages.changeToDisappearingMessageType(
senderConversationModel,
expirationTimer,
senderConversationModel.getExpirationMode()
);
await handleDataExtractionNotification( await handleDataExtractionNotification(
envelope, envelope,
content.dataExtractionNotification as SignalService.DataExtractionNotification, content.dataExtractionNotification as SignalService.DataExtractionNotification,
expireUpdate || null { expirationTimer, expirationType, messageExpirationFromRetrieve }
); );
perfEnd( perfEnd(
`handleDataExtractionNotification-${envelope.id}`, `handleDataExtractionNotification-${envelope.id}`,
@ -838,7 +847,7 @@ async function handleMessageRequestResponse(
export async function handleDataExtractionNotification( export async function handleDataExtractionNotification(
envelope: EnvelopePlus, envelope: EnvelopePlus,
dataNotificationMessage: SignalService.DataExtractionNotification, dataNotificationMessage: SignalService.DataExtractionNotification,
expireUpdate: DisappearingMessageUpdate | null expireUpdate: ReadyToDisappearMsgUpdate
): Promise<void> { ): Promise<void> {
// we currently don't care about the timestamp included in the field itself, just the timestamp of the envelope // we currently don't care about the timestamp included in the field itself, just the timestamp of the envelope
const { type, timestamp: referencedAttachment } = dataNotificationMessage; const { type, timestamp: referencedAttachment } = dataNotificationMessage;
@ -871,6 +880,7 @@ export async function handleDataExtractionNotification(
source, source,
}, },
}); });
created = DisappearingMessages.getMessageReadyToDisappear( created = DisappearingMessages.getMessageReadyToDisappear(
convo, convo,
created, created,

@ -21,6 +21,7 @@ import {
DisappearingMessageMode, DisappearingMessageMode,
DisappearingMessageType, DisappearingMessageType,
DisappearingMessageUpdate, DisappearingMessageUpdate,
ReadyToDisappearMsgUpdate,
} from './types'; } from './types';
async function destroyMessagesAndUpdateRedux( async function destroyMessagesAndUpdateRedux(
@ -303,17 +304,15 @@ async function checkForExpireUpdateInContentMessage(
convoToUpdate: ConversationModel, convoToUpdate: ConversationModel,
messageExpirationFromRetrieve: number | null messageExpirationFromRetrieve: number | null
): Promise<DisappearingMessageUpdate | undefined> { ): Promise<DisappearingMessageUpdate | undefined> {
const dataMessage = content.dataMessage as SignalService.DataMessage; const dataMessage = content.dataMessage as SignalService.DataMessage | undefined;
// We will only support legacy disappearing messages for a short period before disappearing messages v2 is unlocked // We will only support legacy disappearing messages for a short period before disappearing messages v2 is unlocked
const isDisappearingMessagesV2Released = await ReleasedFeatures.checkIsDisappearMessageV2FeatureReleased(); const isDisappearingMessagesV2Released = await ReleasedFeatures.checkIsDisappearMessageV2FeatureReleased();
const couldBeLegacyContentMessage = couldBeLegacyDisappearingMessageContent(content); const couldBeLegacyContentMessage = couldBeLegacyDisappearingMessageContent(content);
const isLegacyDataMessage = checkIsLegacyDisappearingDataMessage( const isLegacyDataMessage =
couldBeLegacyContentMessage, dataMessage && checkIsLegacyDisappearingDataMessage(couldBeLegacyContentMessage, dataMessage);
dataMessage as SignalService.DataMessage
);
const hasExpirationUpdateFlags = const hasExpirationUpdateFlags =
dataMessage.flags === SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE; dataMessage?.flags === SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
const isLegacyConversationSettingMessage = isDisappearingMessagesV2Released const isLegacyConversationSettingMessage = isDisappearingMessagesV2Released
? (isLegacyDataMessage || couldBeLegacyContentMessage) && hasExpirationUpdateFlags ? (isLegacyDataMessage || couldBeLegacyContentMessage) && hasExpirationUpdateFlags
@ -428,7 +427,7 @@ function getMessageReadyToDisappear(
conversationModel: ConversationModel, conversationModel: ConversationModel,
messageModel: MessageModel, messageModel: MessageModel,
messageFlags: number, messageFlags: number,
expireUpdate?: DisappearingMessageUpdate expireUpdate?: ReadyToDisappearMsgUpdate
) { ) {
if (conversationModel.isPublic()) { if (conversationModel.isPublic()) {
throw Error( throw Error(

@ -34,3 +34,8 @@ export type DisappearingMessageUpdate = {
isDisappearingMessagesV2Released?: boolean; isDisappearingMessagesV2Released?: boolean;
messageExpirationFromRetrieve: number | null; messageExpirationFromRetrieve: number | null;
}; };
export type ReadyToDisappearMsgUpdate = Pick<
DisappearingMessageUpdate,
'expirationType' | 'expirationTimer' | 'messageExpirationFromRetrieve'
>;

@ -1,10 +1,9 @@
import { ContentMessage } from '..';
import { SignalService } from '../../../../protobuf'; import { SignalService } from '../../../../protobuf';
import { signalservice } from '../../../../protobuf/compiled'; import { signalservice } from '../../../../protobuf/compiled';
import { TTL_DEFAULT } from '../../../constants'; import { TTL_DEFAULT } from '../../../constants';
import { MessageParams } from '../Message'; import { ExpirableMessage, ExpirableMessageParams } from '../ExpirableMessage';
interface CallMessageParams extends MessageParams { interface CallMessageParams extends ExpirableMessageParams {
type: SignalService.CallMessage.Type; type: SignalService.CallMessage.Type;
sdpMLineIndexes?: Array<number>; sdpMLineIndexes?: Array<number>;
sdpMids?: Array<string>; sdpMids?: Array<string>;
@ -12,7 +11,7 @@ interface CallMessageParams extends MessageParams {
uuid: string; uuid: string;
} }
export class CallMessage extends ContentMessage { export class CallMessage extends ExpirableMessage {
public readonly type: signalservice.CallMessage.Type; public readonly type: signalservice.CallMessage.Type;
public readonly sdpMLineIndexes?: Array<number>; public readonly sdpMLineIndexes?: Array<number>;
public readonly sdpMids?: Array<string>; public readonly sdpMids?: Array<string>;

@ -2,7 +2,6 @@ import { getMessageQueue } from '../../..';
import { SignalService } from '../../../../protobuf'; import { SignalService } from '../../../../protobuf';
import { SnodeNamespaces } from '../../../apis/snode_api/namespaces'; import { SnodeNamespaces } from '../../../apis/snode_api/namespaces';
import { getConversationController } from '../../../conversations'; import { getConversationController } from '../../../conversations';
import { DisappearingMessages } from '../../../disappearing_messages';
import { PubKey } from '../../../types'; import { PubKey } from '../../../types';
import { UserUtils } from '../../../utils'; import { UserUtils } from '../../../utils';
import { ExpirableMessage, ExpirableMessageParams } from '../ExpirableMessage'; import { ExpirableMessage, ExpirableMessageParams } from '../ExpirableMessage';
@ -55,18 +54,12 @@ export const sendDataExtractionNotification = async (
return; return;
} }
const expireTimer = convo.getExpireTimer(); // DataExtractionNotification are expiring with the recipient, so don't include ours
const expirationType = DisappearingMessages.changeToDisappearingMessageType(
convo,
expireTimer,
convo.getExpirationMode()
);
const dataExtractionNotificationMessage = new DataExtractionNotificationMessage({ const dataExtractionNotificationMessage = new DataExtractionNotificationMessage({
referencedAttachmentTimestamp, referencedAttachmentTimestamp,
timestamp: Date.now(), timestamp: Date.now(),
expirationType, expirationType: null,
expireTimer, expireTimer: null,
}); });
const pubkey = PubKey.cast(conversationId); const pubkey = PubKey.cast(conversationId);

@ -417,6 +417,8 @@ async function createOfferAndSendIt(recipient: string) {
type: SignalService.CallMessage.Type.OFFER, type: SignalService.CallMessage.Type.OFFER,
sdps: [overridenSdps], sdps: [overridenSdps],
uuid: currentCallUUID, uuid: currentCallUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
window.log.info(`sending '${offer.type}'' with callUUID: ${currentCallUUID}`); window.log.info(`sending '${offer.type}'' with callUUID: ${currentCallUUID}`);
@ -503,6 +505,8 @@ export async function USER_callRecipient(recipient: string) {
timestamp: now, timestamp: now,
type: SignalService.CallMessage.Type.PRE_OFFER, type: SignalService.CallMessage.Type.PRE_OFFER,
uuid: currentCallUUID, uuid: currentCallUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
window.log.info('Sending preOffer message to ', ed25519Str(recipient)); window.log.info('Sending preOffer message to ', ed25519Str(recipient));
@ -527,7 +531,7 @@ export async function USER_callRecipient(recipient: string) {
if ( if (
expirationMode === 'legacy' || expirationMode === 'legacy' ||
expirationMode === 'deleteAfterSend' || expirationMode === 'deleteAfterSend' ||
expirationMode === 'deleteAfterRead' // we are the one iniating the call, so that message is already read expirationMode === 'deleteAfterRead' // we are the one initiaing the call, so that message is already read
) { ) {
expirationStartTimestamp = DisappearingMessages.setExpirationStartTimestamp( expirationStartTimestamp = DisappearingMessages.setExpirationStartTimestamp(
expirationMode, expirationMode,
@ -537,6 +541,7 @@ export async function USER_callRecipient(recipient: string) {
} }
} }
// Locally, that message must expire
await calledConvo?.addSingleOutgoingMessage({ await calledConvo?.addSingleOutgoingMessage({
callNotificationType: 'started-call', callNotificationType: 'started-call',
sent_at: now, sent_at: now,
@ -610,6 +615,8 @@ const iceSenderDebouncer = _.debounce(async (recipient: string) => {
sdpMids: validCandidates.map(c => c.sdpMid), sdpMids: validCandidates.map(c => c.sdpMid),
sdps: validCandidates.map(c => c.candidate), sdps: validCandidates.map(c => c.candidate),
uuid: currentCallUUID, uuid: currentCallUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
window.log.info( window.log.info(
@ -957,6 +964,8 @@ export async function rejectCallAlreadyAnotherCall(fromSender: string, forcedUUI
type: SignalService.CallMessage.Type.END_CALL, type: SignalService.CallMessage.Type.END_CALL,
timestamp: Date.now(), timestamp: Date.now(),
uuid: forcedUUID, uuid: forcedUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
await sendCallMessageAndSync(rejectCallMessage, fromSender); await sendCallMessageAndSync(rejectCallMessage, fromSender);
@ -980,6 +989,8 @@ export async function USER_rejectIncomingCallRequest(fromSender: string) {
type: SignalService.CallMessage.Type.END_CALL, type: SignalService.CallMessage.Type.END_CALL,
timestamp: Date.now(), timestamp: Date.now(),
uuid: aboutCallUUID, uuid: aboutCallUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
// sync the reject event so our other devices remove the popup too // sync the reject event so our other devices remove the popup too
await sendCallMessageAndSync(endCallMessage, fromSender); await sendCallMessageAndSync(endCallMessage, fromSender);
@ -1022,6 +1033,8 @@ export async function USER_hangup(fromSender: string) {
type: SignalService.CallMessage.Type.END_CALL, type: SignalService.CallMessage.Type.END_CALL,
timestamp: Date.now(), timestamp: Date.now(),
uuid: currentCallUUID, uuid: currentCallUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
void getMessageQueue().sendToPubKeyNonDurably({ void getMessageQueue().sendToPubKeyNonDurably({
pubkey: PubKey.cast(fromSender), pubkey: PubKey.cast(fromSender),
@ -1098,6 +1111,8 @@ async function buildAnswerAndSendIt(sender: string) {
type: SignalService.CallMessage.Type.ANSWER, type: SignalService.CallMessage.Type.ANSWER,
sdps: [answerSdp], sdps: [answerSdp],
uuid: currentCallUUID, uuid: currentCallUUID,
expirationType: null, // Note: CallMessages are expiring based on the recipient's side expiration setting
expireTimer: null,
}); });
window.log.info('sending ANSWER MESSAGE and sync'); window.log.info('sending ANSWER MESSAGE and sync');
@ -1272,6 +1287,8 @@ async function addMissedCallMessage(callerPubkey: string, sentAt: number) {
await incomingCallConversation.unhideIfNeeded(false); await incomingCallConversation.unhideIfNeeded(false);
} }
// Note: Missed call messages are expiring with our side of the conversation settings.
const expirationMode = incomingCallConversation.getExpirationMode(); const expirationMode = incomingCallConversation.getExpirationMode();
const expireTimer = incomingCallConversation.getExpireTimer() || 0; const expireTimer = incomingCallConversation.getExpireTimer() || 0;
let expirationType; let expirationType;

Loading…
Cancel
Save