fix: broken state adding lastDisappearingMessage to chatmsg

pull/2940/head
Audric Ackermann 6 months ago
parent 23998065ea
commit d1068983bb

@ -1830,8 +1830,9 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
identifier: id,
timestamp: sentAt,
attachments,
expirationType: message.getExpirationType(),
expirationType: message.getExpirationType() ?? 'unknown',
expireTimer: message.getExpireTimer(),
lastDisappearingMessageChangeTimestamp: this.getLastDisappearingMessageChangeTimestamp(),
preview: preview ? [preview] : [],
quote,
lokiProfile: UserUtils.getOurProfile(),

@ -291,12 +291,14 @@ async function checkForExpireUpdateInContentMessage(
couldBeLegacyContentMessage,
dataMessage as SignalService.DataMessage
);
const hasExpirationUpdateFlags =
dataMessage.flags === SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
const isLegacyConversationSettingMessage = isDisappearingMessagesV2Released
? (isLegacyDataMessage ||
(couldBeLegacyContentMessage && !content.lastDisappearingMessageChangeTimestamp)) &&
dataMessage.flags === SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE
: couldBeLegacyContentMessage &&
dataMessage.flags === SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
hasExpirationUpdateFlags
: couldBeLegacyContentMessage && hasExpirationUpdateFlags;
const expirationTimer = isLegacyDataMessage
? Number(dataMessage.expireTimer)
@ -315,15 +317,17 @@ async function checkForExpireUpdateInContentMessage(
// NOTE if we are checking an outgoing content message then the conversation's lastDisappearingMessageChangeTimestamp has just been set to match the content message so it can't be outdated if equal
if (
convoToUpdate.getLastDisappearingMessageChangeTimestamp() &&
lastDisappearingMessageChangeTimestamp &&
((isOutgoing &&
(lastDisappearingMessageChangeTimestamp &&
convoToUpdate.getLastDisappearingMessageChangeTimestamp() &&
convoToUpdate.getLastDisappearingMessageChangeTimestamp() >
lastDisappearingMessageChangeTimestamp) ||
(!isOutgoing &&
convoToUpdate.getLastDisappearingMessageChangeTimestamp() >
lastDisappearingMessageChangeTimestamp))
(hasExpirationUpdateFlags &&
convoToUpdate.getLastDisappearingMessageChangeTimestamp() ===
lastDisappearingMessageChangeTimestamp)
) {
// Note: when we receive incoming messages, they will all have the same lastDisappearingMessageChangeTimestamp corresponding to when the latest change was made.
// Those incoming messages are *not* outdated, but a change asking for the same change would be outdated.
window.log.debug(
`[checkForExpireUpdateInContentMessage] This is an outdated ${
isOutgoing ? 'outgoing' : 'incoming'

@ -444,6 +444,9 @@ async function generateAndSendNewEncryptionKeyPair(
groupId: toHex(groupId),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
encryptedKeyPairs: wrappers,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
distributingClosedGroupEncryptionKeyPairs.set(toHex(groupId), newKeyPair);

@ -5,14 +5,16 @@ import { ContentMessage } from './ContentMessage';
import { MessageParams } from './Message';
export interface ExpirableMessageParams extends MessageParams {
expirationType?: DisappearingMessageType;
expireTimer?: number;
expirationType: DisappearingMessageType | null;
expireTimer: number | null;
lastDisappearingMessageChangeTimestamp: number | null;
}
export class ExpirableMessage extends ContentMessage {
public readonly expirationType?: DisappearingMessageType;
public readonly expirationType: DisappearingMessageType | null;
/** in seconds, 0 means no expiration */
public readonly expireTimer?: number;
public readonly expireTimer: number | null;
public readonly lastDisappearingMessageChangeTimestamp: number | null;
constructor(params: ExpirableMessageParams) {
super({
@ -21,6 +23,7 @@ export class ExpirableMessage extends ContentMessage {
});
this.expirationType = params.expirationType;
this.expireTimer = params.expireTimer;
this.lastDisappearingMessageChangeTimestamp = params.lastDisappearingMessageChangeTimestamp;
}
public contentProto(): SignalService.Content {
@ -35,6 +38,7 @@ export class ExpirableMessage extends ContentMessage {
? SignalService.Content.ExpirationType.UNKNOWN
: undefined,
expirationTimer: this.expireTimer && this.expireTimer > -1 ? this.expireTimer : undefined,
lastDisappearingMessageChangeTimestamp: this.lastDisappearingMessageChangeTimestamp,
});
}
@ -51,7 +55,7 @@ export class ExpirableMessage extends ContentMessage {
}
public getDisappearingMessageType(): DisappearingMessageType | undefined {
return this.expirationType;
return this.expirationType || undefined;
}
public ttl(): number {

@ -1,8 +1,8 @@
import { SignalService } from '../../../../protobuf';
import { MessageParams } from '../Message';
import { ContentMessage } from '..';
import { SignalService } from '../../../../protobuf';
import { signalservice } from '../../../../protobuf/compiled';
import { TTL_DEFAULT } from '../../../constants';
import { MessageParams } from '../Message';
interface CallMessageParams extends MessageParams {
type: SignalService.CallMessage.Type;
@ -20,7 +20,7 @@ export class CallMessage extends ContentMessage {
public readonly uuid: string;
constructor(params: CallMessageParams) {
super({ timestamp: params.timestamp, identifier: params.identifier });
super(params);
this.type = params.type;
this.sdpMLineIndexes = params.sdpMLineIndexes;
this.sdpMids = params.sdpMids;

@ -7,7 +7,6 @@ import { ExpirableMessageParams } from '../ExpirableMessage';
interface ExpirationTimerUpdateMessageParams extends ExpirableMessageParams {
groupId?: string | PubKey;
syncTarget?: string | PubKey;
lastDisappearingMessageChangeTimestamp?: number;
}
// NOTE legacy messages used a data message for the expireTimer.
@ -16,7 +15,6 @@ interface ExpirationTimerUpdateMessageParams extends ExpirableMessageParams {
export class ExpirationTimerUpdateMessage extends DataMessage {
public readonly groupId?: PubKey;
public readonly syncTarget?: string;
public readonly lastDisappearingMessageChangeTimestamp?: number;
constructor(params: ExpirationTimerUpdateMessageParams) {
super({
@ -24,10 +22,9 @@ export class ExpirationTimerUpdateMessage extends DataMessage {
identifier: params.identifier,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
lastDisappearingMessageChangeTimestamp: params.lastDisappearingMessageChangeTimestamp,
});
this.lastDisappearingMessageChangeTimestamp = params.lastDisappearingMessageChangeTimestamp;
const { groupId } = params;
this.groupId = groupId ? PubKey.cast(groupId) : undefined;
this.syncTarget = params.syncTarget ? PubKey.cast(params.syncTarget).key : undefined;

@ -10,11 +10,7 @@ export class ClosedGroupAddedMembersMessage extends ClosedGroupMessage {
private readonly addedMembers: Array<string>;
constructor(params: ClosedGroupAddedMembersMessageParams) {
super({
timestamp: params.timestamp,
identifier: params.identifier,
groupId: params.groupId,
});
super(params);
this.addedMembers = params.addedMembers;
if (!this.addedMembers?.length) {
throw new Error('addedMembers cannot be empty');

@ -11,11 +11,7 @@ export class ClosedGroupEncryptionPairMessage extends ClosedGroupMessage {
>;
constructor(params: ClosedGroupEncryptionPairMessageParams) {
super({
timestamp: params.timestamp,
identifier: params.identifier,
groupId: params.groupId,
});
super(params);
this.encryptedKeyPairs = params.encryptedKeyPairs;
if (this.encryptedKeyPairs.length === 0) {
throw new Error('EncryptedKeyPairs cannot be empty');

@ -15,6 +15,7 @@ export abstract class ClosedGroupMessage extends ExpirableMessage {
identifier: params.identifier,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
lastDisappearingMessageChangeTimestamp: params.lastDisappearingMessageChangeTimestamp,
});
this.groupId = PubKey.cast(params.groupId);

@ -9,11 +9,7 @@ export class ClosedGroupNameChangeMessage extends ClosedGroupMessage {
private readonly name: string;
constructor(params: ClosedGroupNameChangeMessageParams) {
super({
timestamp: params.timestamp,
identifier: params.identifier,
groupId: params.groupId,
});
super(params);
this.name = params.name;
if (this.name.length === 0) {
throw new Error('name cannot be empty');

@ -1,7 +1,7 @@
import { SignalService } from '../../../../../protobuf';
import { ClosedGroupMessage, ClosedGroupMessageParams } from './ClosedGroupMessage';
import { fromHexToArray } from '../../../../utils/String';
import { ECKeyPair } from '../../../../../receiver/keypairs';
import { fromHexToArray } from '../../../../utils/String';
import { ClosedGroupMessage, ClosedGroupMessageParams } from './ClosedGroupMessage';
export interface ClosedGroupNewMessageParams extends ClosedGroupMessageParams {
name: string;
@ -22,6 +22,7 @@ export class ClosedGroupNewMessage extends ClosedGroupMessage {
timestamp: params.timestamp,
identifier: params.identifier,
groupId: params.groupId,
lastDisappearingMessageChangeTimestamp: params.lastDisappearingMessageChangeTimestamp,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
});

@ -14,6 +14,9 @@ export class ClosedGroupRemovedMembersMessage extends ClosedGroupMessage {
timestamp: params.timestamp,
identifier: params.identifier,
groupId: params.groupId,
lastDisappearingMessageChangeTimestamp: params.lastDisappearingMessageChangeTimestamp,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
});
this.removedMembers = params.removedMembers;
if (!this.removedMembers?.length) {

@ -7,7 +7,11 @@ import {
} from '../controlMessage/group/ClosedGroupMessage';
import { VisibleMessage } from './VisibleMessage';
interface ClosedGroupVisibleMessageParams extends ClosedGroupMessageParams {
interface ClosedGroupVisibleMessageParams
extends Omit<
ClosedGroupMessageParams,
'expireTimer' | 'expirationType' | 'lastDisappearingMessageChangeTimestamp'
> {
groupId: PubKey;
chatMessage: VisibleMessage;
}
@ -20,8 +24,10 @@ export class ClosedGroupVisibleMessage extends ClosedGroupMessage {
timestamp: params.chatMessage.timestamp,
identifier: params.identifier ?? params.chatMessage.identifier,
groupId: params.groupId,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
expirationType: params.chatMessage.expirationType,
expireTimer: params.chatMessage.expireTimer,
lastDisappearingMessageChangeTimestamp:
params.chatMessage.lastDisappearingMessageChangeTimestamp ?? null,
});
this.chatMessage = params.chatMessage;

@ -16,6 +16,7 @@ export class GroupInvitationMessage extends VisibleMessage {
identifier: params.identifier,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
lastDisappearingMessageChangeTimestamp: params.lastDisappearingMessageChangeTimestamp,
});
this.url = params.url;
this.name = params.name;

@ -6,14 +6,19 @@ import { VisibleMessage, VisibleMessageParams } from './VisibleMessage';
// eslint-disable-next-line @typescript-eslint/ban-types
export type OpenGroupVisibleMessageParams = Omit<
VisibleMessageParams,
'expirationType' | 'expireTimer'
'expirationType' | 'expireTimer' | 'lastDisappearingMessageChangeTimestamp'
>;
export class OpenGroupVisibleMessage extends VisibleMessage {
private readonly blocksCommunityMessageRequests: boolean;
constructor(params: OpenGroupVisibleMessageParams) {
super(params);
super({
...params,
lastDisappearingMessageChangeTimestamp: null,
expirationType: null,
expireTimer: null,
});
// they are the opposite of each others
this.blocksCommunityMessageRequests = !Storage.get(SettingsKey.hasBlindedMsgRequestsEnabled);
}

@ -91,6 +91,7 @@ export class VisibleMessage extends ExpirableMessage {
identifier: params.identifier,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
lastDisappearingMessageChangeTimestamp: params.lastDisappearingMessageChangeTimestamp,
});
this.attachments = params.attachments;
this.body = params.body;

@ -332,7 +332,8 @@ const buildSyncVisibleMessage = (
preview,
syncTarget,
expireTimer: expireUpdate?.expirationTimer || dataMessageExpireTimer,
expirationType: expireUpdate?.expirationType,
expirationType: expireUpdate?.expirationType || null,
lastDisappearingMessageChangeTimestamp: null,
});
};
@ -353,7 +354,7 @@ const buildSyncExpireTimerMessage = (
timestamp,
expirationType,
expireTimer,
lastDisappearingMessageChangeTimestamp,
lastDisappearingMessageChangeTimestamp: lastDisappearingMessageChangeTimestamp ?? null,
syncTarget,
});
};

@ -11,10 +11,17 @@ import {
VisibleMessage,
} from '../../../../session/messages/outgoing/visibleMessage/VisibleMessage';
const sharedNoExpire = {
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
};
describe('VisibleMessage', () => {
it('can create empty message with just a timestamp', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
...sharedNoExpire,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -26,6 +33,7 @@ describe('VisibleMessage', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
body: 'body',
...sharedNoExpire,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -35,6 +43,7 @@ describe('VisibleMessage', () => {
it('can create a disappear after read message', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
...sharedNoExpire,
expirationType: 'deleteAfterRead',
expireTimer: 300,
});
@ -53,6 +62,7 @@ describe('VisibleMessage', () => {
it('can create a disappear after send message', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
...sharedNoExpire,
expirationType: 'deleteAfterSend',
expireTimer: 60,
});
@ -79,6 +89,7 @@ describe('VisibleMessage', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
lokiProfile,
...sharedNoExpire,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -98,6 +109,7 @@ describe('VisibleMessage', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
quote,
...sharedNoExpire,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -115,6 +127,7 @@ describe('VisibleMessage', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
preview: previews,
...sharedNoExpire,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -139,6 +152,7 @@ describe('VisibleMessage', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
attachments,
...sharedNoExpire,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -153,6 +167,7 @@ describe('VisibleMessage', () => {
it('correct ttl', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
...sharedNoExpire,
});
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.CONTENT_MESSAGE);
});
@ -160,7 +175,9 @@ describe('VisibleMessage', () => {
it('has an identifier', () => {
const message = new VisibleMessage({
timestamp: Date.now(),
...sharedNoExpire,
});
expect(message.identifier).to.not.equal(null, 'identifier cannot be null');
expect(message.identifier).to.not.equal(undefined, 'identifier cannot be undefined');
});

@ -16,6 +16,9 @@ describe('GroupInvitationMessage', () => {
timestamp,
url,
name,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
});

@ -18,11 +18,17 @@ describe('ClosedGroupVisibleMessage', () => {
const chatMessage = new VisibleMessage({
timestamp,
body: 'body',
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
const message = new ClosedGroupVisibleMessage({
groupId,
timestamp,
chatMessage,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
@ -48,11 +54,17 @@ describe('ClosedGroupVisibleMessage', () => {
const timestamp = Date.now();
const chatMessage = new VisibleMessage({
timestamp,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
const message = new ClosedGroupVisibleMessage({
groupId,
timestamp,
chatMessage,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.CONTENT_MESSAGE);
});
@ -61,11 +73,17 @@ describe('ClosedGroupVisibleMessage', () => {
const timestamp = Date.now();
const chatMessage = new VisibleMessage({
timestamp,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
const message = new ClosedGroupVisibleMessage({
groupId,
timestamp,
chatMessage,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
expect(message.identifier).to.not.equal(null, 'identifier cannot be null');
expect(message.identifier).to.not.equal(undefined, 'identifier cannot be undefined');
@ -77,12 +95,18 @@ describe('ClosedGroupVisibleMessage', () => {
timestamp,
body: 'body',
identifier: 'chatMessage',
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
const message = new ClosedGroupVisibleMessage({
groupId,
timestamp,
chatMessage,
identifier: 'closedGroupMessage',
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
expect(message.identifier).to.be.equal('closedGroupMessage');
});
@ -93,11 +117,17 @@ describe('ClosedGroupVisibleMessage', () => {
timestamp,
body: 'body',
identifier: 'chatMessage',
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
const message = new ClosedGroupVisibleMessage({
groupId,
timestamp,
chatMessage,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
expect(message.identifier).to.be.equal('chatMessage');
});

@ -29,6 +29,12 @@ chai.use(chaiAsPromised as any);
const { expect } = chai;
const sharedNoExpire = {
expireTimer: null,
expirationType: null,
lastDisappearingMessageChangeTimestamp: null,
};
describe('Message Utils', () => {
afterEach(() => {
Sinon.restore();
@ -111,6 +117,7 @@ describe('Message Utils', () => {
groupId,
timestamp: Date.now(),
chatMessage,
...sharedNoExpire,
});
const rawMessage = await MessageUtils.toRawMessage(
@ -144,6 +151,7 @@ describe('Message Utils', () => {
admins: [member],
groupId: TestUtils.generateFakePubKey().key,
keypair: TestUtils.generateFakeECKeyPair(),
...sharedNoExpire,
expireTimer: 0,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg, SnodeNamespaces.UserMessages);
@ -157,6 +165,7 @@ describe('Message Utils', () => {
timestamp: Date.now(),
name: 'df',
groupId: TestUtils.generateFakePubKey().key,
...sharedNoExpire,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg, SnodeNamespaces.UserMessages);
expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
@ -169,6 +178,7 @@ describe('Message Utils', () => {
timestamp: Date.now(),
addedMembers: [TestUtils.generateFakePubKey().key],
groupId: TestUtils.generateFakePubKey().key,
...sharedNoExpire,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg, SnodeNamespaces.UserMessages);
expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
@ -181,6 +191,7 @@ describe('Message Utils', () => {
timestamp: Date.now(),
removedMembers: [TestUtils.generateFakePubKey().key],
groupId: TestUtils.generateFakePubKey().key,
...sharedNoExpire,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg, SnodeNamespaces.UserMessages);
expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
@ -202,6 +213,7 @@ describe('Message Utils', () => {
timestamp: Date.now(),
groupId: TestUtils.generateFakePubKey().key,
encryptedKeyPairs: fakeWrappers,
...sharedNoExpire,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg, SnodeNamespaces.UserMessages);
expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
@ -223,6 +235,7 @@ describe('Message Utils', () => {
timestamp: Date.now(),
groupId: TestUtils.generateFakePubKey().key,
encryptedKeyPairs: fakeWrappers,
...sharedNoExpire,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg, SnodeNamespaces.UserMessages);
expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE);

@ -33,8 +33,9 @@ export function generateVisibleMessage({
timestamp: timestamp || Date.now(),
attachments: undefined,
quote: undefined,
expirationType: undefined,
expireTimer: undefined,
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
lokiProfile: undefined,
preview: undefined,
});
@ -95,6 +96,9 @@ export function generateClosedGroupMessage(
groupId: groupId ? PubKey.cast(groupId) : generateFakePubKey(),
timestamp: timestamp || Date.now(),
chatMessage: generateVisibleMessage(),
expirationType: null,
expireTimer: null,
lastDisappearingMessageChangeTimestamp: null,
});
}
@ -153,10 +157,10 @@ export function generateDisappearingVisibleMessage({
return new ExpirationTimerUpdateMessage({
identifier: identifier ?? uuid(),
timestamp: timestamp || Date.now(),
expirationType: expirationTimerUpdate.expirationType,
expirationType: expirationTimerUpdate.expirationType || null,
expireTimer: expirationTimerUpdate.expireTimer,
lastDisappearingMessageChangeTimestamp:
expirationTimerUpdate.lastDisappearingMessageChangeTimestamp,
expirationTimerUpdate.lastDisappearingMessageChangeTimestamp || null,
});
}
@ -166,10 +170,11 @@ export function generateDisappearingVisibleMessage({
timestamp: timestamp || Date.now(),
attachments: undefined,
quote: undefined,
expirationType,
expireTimer,
expirationType: expirationType ?? null,
expireTimer: expireTimer ?? null,
lokiProfile: undefined,
preview: undefined,
lastDisappearingMessageChangeTimestamp: null,
});
}

Loading…
Cancel
Save