Disallow conversation model creation during import

Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
automated-signal 2025-01-28 15:16:43 -06:00 committed by GitHub
parent 55464045a4
commit 9f5602af05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 60 additions and 8 deletions

View file

@ -150,6 +150,7 @@ export function start(): void {
export class ConversationController {
#_initialFetchComplete = false;
#isReadOnly = false;
private _initialPromise: undefined | Promise<void>;
@ -291,6 +292,10 @@ export class ConversationController {
return conversation;
}
if (this.#isReadOnly) {
throw new Error('ConversationController is read-only');
}
const id = generateUuid();
if (type === 'group') {
@ -1299,6 +1304,16 @@ export class ConversationController {
);
}
setReadOnly(value: boolean): void {
if (this.#isReadOnly === value) {
log.warn(`ConversationController: already at readOnly=${value}`);
return;
}
log.info(`ConversationController: readOnly=${value}`);
this.#isReadOnly = value;
}
reset(): void {
delete this._initialPromise;
this.#_initialFetchComplete = false;

View file

@ -1513,10 +1513,10 @@ export async function startApp(): Promise<void> {
});
void updateExpiringMessagesService();
void tapToViewMessagesDeletionService.update();
tapToViewMessagesDeletionService.update();
window.Whisper.events.on('timetravel', () => {
void updateExpiringMessagesService();
void tapToViewMessagesDeletionService.update();
tapToViewMessagesDeletionService.update();
});
const isCoreDataValid = Boolean(
@ -1652,6 +1652,8 @@ export async function startApp(): Promise<void> {
const backupDownloadPath = window.storage.get('backupDownloadPath');
if (backupDownloadPath) {
tapToViewMessagesDeletionService.pause();
// Download backup before enabling request handler and storage service
try {
await backupsService.downloadAndImport({
@ -1670,6 +1672,8 @@ export async function startApp(): Promise<void> {
log.error('afterStart: backup download failed, rejecting');
backupReady.reject(error);
throw error;
} finally {
tapToViewMessagesDeletionService.resume();
}
} else {
backupReady.resolve();

View file

@ -342,6 +342,7 @@ export class BackupImportStream extends Writable {
}
// Reset and reload conversations and storage again
window.ConversationController.setReadOnly(false);
window.ConversationController.reset();
await window.ConversationController.load();

View file

@ -350,6 +350,8 @@ export class BackupsService {
await DataWriter.disableMessageInsertTriggers();
try {
window.ConversationController.setReadOnly(true);
const importStream = await BackupImportStream.create(backupType);
if (backupType === BackupType.Ciphertext) {
const { aesKey, macKey } = getKeyMaterial(
@ -440,6 +442,7 @@ export class BackupsService {
throw error;
} finally {
window.ConversationController.setReadOnly(false);
this.#isRunning = false;
await DataWriter.enableMessageInsertTriggersAndBackfill();

View file

@ -10,6 +10,7 @@ import { strictAssert } from '../util/assert';
import { toBoundedDate } from '../util/timestamp';
import { getMessageIdForLogging } from '../util/idForLogging';
import { eraseMessageContents } from '../util/cleanup';
import { drop } from '../util/drop';
import { MessageModel } from '../models/messages';
async function eraseTapToViewMessages() {
@ -53,12 +54,38 @@ async function eraseTapToViewMessages() {
}
class TapToViewMessagesDeletionService {
public update: () => Promise<void>;
#timeout?: ReturnType<typeof setTimeout>;
#isPaused = false;
#debouncedUpdate = debounce(this.#checkTapToViewMessages);
constructor() {
this.update = debounce(this.#checkTapToViewMessages, 1000);
update() {
drop(this.#debouncedUpdate());
}
pause(): void {
if (this.#isPaused) {
window.SignalContext.log.warn('checkTapToViewMessages: already paused');
return;
}
window.SignalContext.log.info('checkTapToViewMessages: pause');
this.#isPaused = true;
clearTimeoutIfNecessary(this.#timeout);
this.#timeout = undefined;
}
resume(): void {
if (!this.#isPaused) {
window.SignalContext.log.warn('checkTapToViewMessages: not paused');
return;
}
window.SignalContext.log.info('checkTapToViewMessages: resuming');
this.#isPaused = false;
this.#debouncedUpdate.cancel();
this.update();
}
async #checkTapToViewMessages() {
@ -89,8 +116,10 @@ class TapToViewMessagesDeletionService {
clearTimeoutIfNecessary(this.#timeout);
this.#timeout = setTimeout(async () => {
await eraseTapToViewMessages();
void this.update();
if (!this.#isPaused && !window.SignalContext.isTestOrMockEnvironment()) {
await eraseTapToViewMessages();
}
this.update();
}, wait);
}
}