From ec6a5995dbd45157e5183a96f8d91da33b028d66 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 19 Mar 2021 11:58:36 +1100 Subject: [PATCH] sync expire timer updates --- ts/models/conversation.ts | 7 +- ts/models/message.ts | 8 +- ts/models/messageType.ts | 2 - ts/receiver/queuedJob.ts | 56 ++------- ts/session/group/index.ts | 11 -- .../ExpirationTimerUpdateMessage.ts | 6 +- .../outgoing/visibleMessage/VisibleMessage.ts | 57 +-------- ts/session/sending/MessageQueue.ts | 3 +- ts/session/utils/syncUtils.ts | 111 ++++++++++++++++++ 9 files changed, 137 insertions(+), 124 deletions(-) diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 2871c0b17..a5f73b657 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -841,12 +841,14 @@ export class ConversationModel extends Backbone.Model { providedExpireTimer: any, providedSource?: string, receivedAt?: number, // is set if it comes from outside - options: any = {} + options: { + fromSync?: boolean; + } = {} ) { let expireTimer = providedExpireTimer; let source = providedSource; - _.defaults(options, { fromSync: false, fromGroupUpdate: false }); + _.defaults(options, { fromSync: false }); if (!expireTimer) { expireTimer = null; @@ -887,7 +889,6 @@ export class ConversationModel extends Backbone.Model { expireTimer, source, fromSync: options.fromSync, - fromGroupUpdate: options.fromGroupUpdate, }, expireTimer: 0, type: isOutgoing ? 'outgoing' : ('incoming' as MessageModelType), diff --git a/ts/models/message.ts b/ts/models/message.ts index cc24a7c22..212bb0328 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -7,6 +7,7 @@ import { getMessageQueue, Types, Utils } from '../../ts/session'; import { ConversationController } from '../../ts/session/conversations'; import { MessageController } from '../../ts/session/messages'; import { + ContentMessage, DataMessage, OpenGroupMessage, } from '../../ts/session/messages/outgoing'; @@ -24,6 +25,7 @@ import { saveMessage } from '../../ts/data/data'; import { ConversationModel } from './conversation'; import { actions as conversationActions } from '../state/ducks/conversations'; import { VisibleMessage } from '../session/messages/outgoing/visibleMessage/VisibleMessage'; +import { buildSyncMessage } from '../session/utils/syncUtils'; export class MessageModel extends Backbone.Model { public propsForTimerNotification: any; @@ -1027,13 +1029,15 @@ export class MessageModel extends Backbone.Model { // if this message needs to be synced if ( (dataMessage.body && dataMessage.body.length) || - dataMessage.attachments.length + dataMessage.attachments.length || + dataMessage.flags === + SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE ) { const conversation = this.getConversation(); if (!conversation) { throw new Error('Cannot trigger syncMessage with unknown convo.'); } - const syncMessage = VisibleMessage.buildSyncMessage( + const syncMessage = buildSyncMessage( this.id, dataMessage, conversation.id, diff --git a/ts/models/messageType.ts b/ts/models/messageType.ts index 53ea4b076..bc0249e2d 100644 --- a/ts/models/messageType.ts +++ b/ts/models/messageType.ts @@ -48,7 +48,6 @@ export interface MessageAttributes { expireTimer: number; source: string; fromSync?: boolean; - fromGroupUpdate?: boolean; }; /** * 1 means unread, 0 or anything else is read. @@ -138,7 +137,6 @@ export interface MessageAttributesOptionals { expireTimer: number; source: string; fromSync?: boolean; - fromGroupUpdate?: boolean; }; unread?: number; group?: any; diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 276cdd00b..aa18c5123 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -196,39 +196,6 @@ async function copyFromQuotedMessage( } } -// Handle expiration timer as part of a regular message -async function handleExpireTimer( - source: string, - message: MessageModel, - expireTimer: number, - conversation: ConversationModel -) { - const oldValue = conversation.get('expireTimer'); - - if (expireTimer) { - message.set({ expireTimer }); - - if (expireTimer !== oldValue) { - await conversation.updateExpirationTimer( - expireTimer, - source, - message.get('sent_at') || message.get('received_at'), - { - fromGroupUpdate: message.isGroupUpdate(), // WHAT DOES GROUP UPDATE HAVE TO DO WITH THIS??? - } - ); - } - } else if (oldValue && !message.isGroupUpdate()) { - // We only turn off timers if it's not a group update - await conversation.updateExpirationTimer( - null, - source, - message.get('received_at'), - {} - ); - } -} - function handleLinkPreviews( messageBody: string, messagePreview: any, @@ -363,7 +330,7 @@ async function handleRegularMessage( // `upgradeMessageSchema` only seems to add `schemaVersion: 10` to the message const dataMessage = await upgradeMessageSchema(initialMessage); - const now = new Date().getTime(); + const now = Date.now(); // Medium groups might have `group` set even if with group chat messages... if (dataMessage.group && !conversation.isMediumGroup()) { @@ -383,6 +350,7 @@ async function handleRegularMessage( } handleLinkPreviews(dataMessage.body, dataMessage.preview, message); + const existingExpireTimer = conversation.get('expireTimer'); message.set({ flags: dataMessage.flags, @@ -399,15 +367,14 @@ async function handleRegularMessage( errors: [], }); - conversation.set({ active_at: now }); + if (existingExpireTimer) { + message.set({ expireTimer: existingExpireTimer }); + message.set({ expirationStartTimestamp: now }); + } - // Handle expireTimer found directly as part of a regular message - await handleExpireTimer( - source, - message, - dataMessage.expireTimer, - conversation - ); + conversation.set({ active_at: now }); + // Expire timer updates are now explicit. + // We don't handle an expire timer from a incoming message except if it is an ExpireTimerUpdate message. const ourPubKey = PubKey.cast(ourNumber); @@ -478,10 +445,7 @@ async function handleExpirationTimerUpdate( await conversation.updateExpirationTimer( expireTimer, source, - message.get('received_at'), - { - fromGroupUpdate: message.isGroupUpdate(), // WHAT DOES GROUP UPDATE HAVE TO DO WITH THIS??? - } + message.get('received_at') ); } diff --git a/ts/session/group/index.ts b/ts/session/group/index.ts index 7c380544f..8293d8dae 100644 --- a/ts/session/group/index.ts +++ b/ts/session/group/index.ts @@ -449,17 +449,6 @@ async function sendAddedMembers( expireTimer, }); - // if an expire timer is set, we have to send it to the joining members - // let expirationTimerMessage: ExpirationTimerUpdateMessage | undefined; - // if (expireTimer && expireTimer > 0) { - // const expireUpdate = { - // timestamp: Date.now(), - // expireTimer, - // groupId: groupId, - // }; - - // expirationTimerMessage = new ExpirationTimerUpdateMessage(expireUpdate); - // } const promises = addedMembers.map(async m => { await ConversationController.getInstance().getOrCreateAndWait(m, 'private'); const memberPubKey = PubKey.cast(m); diff --git a/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts b/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts index 5be9112ef..72aff13ae 100644 --- a/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts +++ b/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts @@ -13,7 +13,7 @@ interface ExpirationTimerUpdateMessageParams extends MessageParams { export class ExpirationTimerUpdateMessage extends DataMessage { public readonly groupId?: PubKey; - public readonly syncTarget?: PubKey; + public readonly syncTarget?: string; public readonly expireTimer: number | null; constructor(params: ExpirationTimerUpdateMessageParams) { @@ -22,7 +22,7 @@ export class ExpirationTimerUpdateMessage extends DataMessage { const { groupId, syncTarget } = params; this.groupId = groupId ? PubKey.cast(groupId) : undefined; - this.syncTarget = syncTarget ? PubKey.cast(syncTarget) : undefined; + this.syncTarget = syncTarget ? PubKey.cast(syncTarget).key : undefined; } public ttl(): number { @@ -50,7 +50,7 @@ export class ExpirationTimerUpdateMessage extends DataMessage { } if (this.syncTarget) { - data.syncTarget = this.syncTarget.key; + data.syncTarget = this.syncTarget; } if (this.expireTimer) { diff --git a/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts b/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts index ea8038349..311ded641 100644 --- a/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts +++ b/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts @@ -5,6 +5,7 @@ import { DataMessage } from '..'; import { Constants } from '../../..'; import { SignalService } from '../../../../protobuf'; import { LokiProfile } from '../../../../types/Message'; +import { ExpirationTimerUpdateMessage } from '../controlMessage/ExpirationTimerUpdateMessage'; import { MessageParams } from '../Message'; export interface AttachmentPointer { @@ -91,62 +92,6 @@ export class VisibleMessage extends DataMessage { this.syncTarget = params.syncTarget; } - public static buildSyncMessage( - identifier: string, - dataMessage: SignalService.DataMessage, - syncTarget: string, - sentTimestamp: number - ) { - if ( - (dataMessage as any).constructor.name !== 'DataMessage' && - !(dataMessage instanceof SignalService.DataMessage) - ) { - window.log.warn( - 'buildSyncMessage with something else than a DataMessage' - ); - } - - if (!sentTimestamp || !isNumber(sentTimestamp)) { - throw new Error('Tried to build a sync message without a sentTimestamp'); - } - // don't include our profileKey on syncing message. This is to be done by a ConfigurationMessage now - const timestamp = toNumber(sentTimestamp); - const body = dataMessage.body || undefined; - - const wrapToUInt8Array = (buffer: any) => { - if (!buffer) { - return undefined; - } - if (buffer instanceof Uint8Array) { - // Audio messages are already uint8Array - return buffer; - } - return new Uint8Array(buffer.toArrayBuffer()); - }; - const attachments = (dataMessage.attachments || []).map(attachment => { - const key = wrapToUInt8Array(attachment.key); - const digest = wrapToUInt8Array(attachment.digest); - - return { - ...attachment, - key, - digest, - }; - }) as Array; - const quote = (dataMessage.quote as Quote) || undefined; - const preview = (dataMessage.preview as Array) || []; - - return new VisibleMessage({ - identifier, - timestamp, - attachments, - body, - quote, - preview, - syncTarget, - }); - } - public ttl(): number { return Constants.TTL_DEFAULT.REGULAR_MESSAGE; } diff --git a/ts/session/sending/MessageQueue.ts b/ts/session/sending/MessageQueue.ts index 411456a11..e79c1efaa 100644 --- a/ts/session/sending/MessageQueue.ts +++ b/ts/session/sending/MessageQueue.ts @@ -16,6 +16,7 @@ import { ClosedGroupEncryptionPairRequestMessage } from '../messages/outgoing/co import { ClosedGroupNewMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNewMessage'; import { ClosedGroupRemovedMembersMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupRemovedMembersMessage'; import { ClosedGroupVisibleMessage } from '../messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; +import { SyncMessageType } from '../utils/syncUtils'; type ClosedGroupMessageType = | ClosedGroupVisibleMessage @@ -108,7 +109,7 @@ export class MessageQueue { } public async sendSyncMessage( - message?: ContentMessage, + message?: SyncMessageType, sentCb?: (message: RawMessage) => Promise ): Promise { if (!message) { diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index e46c7094d..fcc22fd64 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -21,6 +21,15 @@ import { fromHexToArray, } from './String'; import { fromBase64 } from 'bytebuffer'; +import { SignalService } from '../../protobuf'; +import _ from 'lodash'; +import { + AttachmentPointer, + Preview, + Quote, + VisibleMessage, +} from '../messages/outgoing/visibleMessage/VisibleMessage'; +import { ExpirationTimerUpdateMessage } from '../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; const ITEM_ID_LAST_SYNC_TIMESTAMP = 'lastSyncedTimestamp'; @@ -186,3 +195,105 @@ export const getCurrentConfigurationMessage = async ( contacts, }); }; + +const buildSyncVisibleMessage = ( + identifier: string, + dataMessage: SignalService.DataMessage, + timestamp: number, + syncTarget: string +) => { + const body = dataMessage.body || undefined; + + const wrapToUInt8Array = (buffer: any) => { + if (!buffer) { + return undefined; + } + if (buffer instanceof Uint8Array) { + // Audio messages are already uint8Array + return buffer; + } + return new Uint8Array(buffer.toArrayBuffer()); + }; + const attachments = (dataMessage.attachments || []).map(attachment => { + const key = wrapToUInt8Array(attachment.key); + const digest = wrapToUInt8Array(attachment.digest); + + return { + ...attachment, + key, + digest, + }; + }) as Array; + const quote = (dataMessage.quote as Quote) || undefined; + const preview = (dataMessage.preview as Array) || []; + const expireTimer = dataMessage.expireTimer; + + return new VisibleMessage({ + identifier, + timestamp, + attachments, + body, + quote, + preview, + syncTarget, + expireTimer, + }); +}; + +const buildSyncExpireTimerMessage = ( + identifier: string, + dataMessage: SignalService.DataMessage, + timestamp: number, + syncTarget: string +) => { + const expireTimer = dataMessage.expireTimer; + + return new ExpirationTimerUpdateMessage({ + identifier, + timestamp, + expireTimer, + syncTarget, + }); +}; + +export type SyncMessageType = + | VisibleMessage + | ExpirationTimerUpdateMessage + | ConfigurationMessage; + +export const buildSyncMessage = ( + identifier: string, + dataMessage: SignalService.DataMessage, + syncTarget: string, + sentTimestamp: number +): VisibleMessage | ExpirationTimerUpdateMessage => { + if ( + (dataMessage as any).constructor.name !== 'DataMessage' && + !(dataMessage instanceof SignalService.DataMessage) + ) { + window.log.warn('buildSyncMessage with something else than a DataMessage'); + } + + if (!sentTimestamp || !_.isNumber(sentTimestamp)) { + throw new Error('Tried to build a sync message without a sentTimestamp'); + } + // don't include our profileKey on syncing message. This is to be done by a ConfigurationMessage now + const timestamp = _.toNumber(sentTimestamp); + if ( + dataMessage.flags === + SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE + ) { + return buildSyncExpireTimerMessage( + identifier, + dataMessage, + timestamp, + syncTarget + ); + } + return buildSyncVisibleMessage( + identifier, + dataMessage, + timestamp, + syncTarget + ); +};