eraseAllStorageServiceState: Delete everything, delete in memory
This commit is contained in:
parent
b7b725f74c
commit
90f0f8e255
7 changed files with 143 additions and 36 deletions
|
@ -2970,15 +2970,21 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(`unlinkAndDisconnect: removing configuration, mode ${mode}`);
|
log.info(`unlinkAndDisconnect: removing configuration, mode ${mode}`);
|
||||||
await window.textsecure.storage.protocol.removeAllConfiguration(mode);
|
|
||||||
|
|
||||||
// This was already done in the database with removeAllConfiguration; this does it
|
// First, make changes to conversations in memory
|
||||||
// for all the conversation models in memory.
|
|
||||||
window.getConversations().forEach(conversation => {
|
window.getConversations().forEach(conversation => {
|
||||||
// eslint-disable-next-line no-param-reassign
|
conversation.unset('senderKeyInfo');
|
||||||
delete conversation.attributes.senderKeyInfo;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Then make sure outstanding conversation saves are flushed
|
||||||
|
await window.Signal.Data.flushUpdateConversationBatcher();
|
||||||
|
|
||||||
|
// Then make sure that all previously-outstanding database saves are flushed
|
||||||
|
await window.Signal.Data.getItemById('manifestVersion');
|
||||||
|
|
||||||
|
// Finally, conversations in the database, and delete all config tables
|
||||||
|
await window.textsecure.storage.protocol.removeAllConfiguration(mode);
|
||||||
|
|
||||||
// These three bits of data are important to ensure that the app loads up
|
// These three bits of data are important to ensure that the app loads up
|
||||||
// the conversation list, instead of showing just the QR code screen.
|
// the conversation list, instead of showing just the QR code screen.
|
||||||
if (previousNumberId !== undefined) {
|
if (previousNumberId !== undefined) {
|
||||||
|
@ -3102,10 +3108,17 @@ export async function startApp(): Promise<void> {
|
||||||
log.info(
|
log.info(
|
||||||
'onKeysSync: updated storage service key, erasing state and fetching'
|
'onKeysSync: updated storage service key, erasing state and fetching'
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
await window.storage.put('storageKey', storageServiceKeyBase64);
|
await window.storage.put('storageKey', storageServiceKeyBase64);
|
||||||
await StorageService.eraseAllStorageServiceState({
|
await StorageService.eraseAllStorageServiceState({
|
||||||
keepUnknownFields: true,
|
keepUnknownFields: true,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.info(
|
||||||
|
'onKeysSync: Failed to erase storage service data, starting sync job anyway',
|
||||||
|
Errors.toLogFormat(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await StorageService.runStorageServiceSyncJob();
|
await StorageService.runStorageServiceSyncJob();
|
||||||
|
|
|
@ -69,7 +69,9 @@ import { isSignalConversation } from '../util/isSignalConversation';
|
||||||
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
|
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
eraseStorageServiceStateFromConversations,
|
eraseStorageServiceState,
|
||||||
|
flushUpdateConversationBatcher,
|
||||||
|
getItemById,
|
||||||
updateConversation,
|
updateConversation,
|
||||||
updateConversations,
|
updateConversations,
|
||||||
} = dataInterface;
|
} = dataInterface;
|
||||||
|
@ -1893,12 +1895,12 @@ export function enableStorageService(): void {
|
||||||
storageServiceEnabled = true;
|
storageServiceEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this function is meant to be called before ConversationController is hydrated.
|
|
||||||
// It goes directly to the database, so in-memory conversations will be out of date.
|
|
||||||
export async function eraseAllStorageServiceState({
|
export async function eraseAllStorageServiceState({
|
||||||
keepUnknownFields = false,
|
keepUnknownFields = false,
|
||||||
}: { keepUnknownFields?: boolean } = {}): Promise<void> {
|
}: { keepUnknownFields?: boolean } = {}): Promise<void> {
|
||||||
log.info('storageService.eraseAllStorageServiceState: starting...');
|
log.info('storageService.eraseAllStorageServiceState: starting...');
|
||||||
|
|
||||||
|
// First, update high-level storage service metadata
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
window.storage.remove('manifestVersion'),
|
window.storage.remove('manifestVersion'),
|
||||||
keepUnknownFields
|
keepUnknownFields
|
||||||
|
@ -1906,7 +1908,34 @@ export async function eraseAllStorageServiceState({
|
||||||
: window.storage.remove('storage-service-unknown-records'),
|
: window.storage.remove('storage-service-unknown-records'),
|
||||||
window.storage.remove('storageCredentials'),
|
window.storage.remove('storageCredentials'),
|
||||||
]);
|
]);
|
||||||
await eraseStorageServiceStateFromConversations();
|
|
||||||
|
// Then, we make the changes to records in memory:
|
||||||
|
// - Conversations
|
||||||
|
// - Sticker packs
|
||||||
|
// - Uninstalled sticker packs
|
||||||
|
// - Story distribution lists
|
||||||
|
|
||||||
|
// This call just erases stickers for now. Storage service data is not stored
|
||||||
|
// in memory for Story Distribution Lists. Uninstalled sticker packs are not
|
||||||
|
// kept in memory at all.
|
||||||
|
window.reduxActions.user.eraseStorageServiceState();
|
||||||
|
|
||||||
|
// Conversations. These properties are not present in redux.
|
||||||
|
window.getConversations().forEach(conversation => {
|
||||||
|
conversation.unset('storageID');
|
||||||
|
conversation.unset('needsStorageServiceSync');
|
||||||
|
conversation.unset('storageUnknownFields');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then make sure outstanding conversation saves are flushed
|
||||||
|
await flushUpdateConversationBatcher();
|
||||||
|
|
||||||
|
// Then make sure that all previously-outstanding database saves are flushed
|
||||||
|
await getItemById('manifestVersion');
|
||||||
|
|
||||||
|
// Finally, we update the database directly for all record types:
|
||||||
|
await eraseStorageServiceState();
|
||||||
|
|
||||||
log.info('storageService.eraseAllStorageServiceState: complete');
|
log.info('storageService.eraseAllStorageServiceState: complete');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ const exclusiveInterface: ClientExclusiveInterface = {
|
||||||
|
|
||||||
// Client-side only
|
// Client-side only
|
||||||
|
|
||||||
|
flushUpdateConversationBatcher,
|
||||||
|
|
||||||
shutdown,
|
shutdown,
|
||||||
removeAllMessagesInConversation,
|
removeAllMessagesInConversation,
|
||||||
|
|
||||||
|
@ -458,6 +460,9 @@ const updateConversationBatcher = createBatcher<ConversationType>({
|
||||||
function updateConversation(data: ConversationType): void {
|
function updateConversation(data: ConversationType): void {
|
||||||
updateConversationBatcher.add(data);
|
updateConversationBatcher.add(data);
|
||||||
}
|
}
|
||||||
|
async function flushUpdateConversationBatcher(): Promise<void> {
|
||||||
|
await updateConversationBatcher.flushAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
async function updateConversations(
|
async function updateConversations(
|
||||||
array: Array<ConversationType>
|
array: Array<ConversationType>
|
||||||
|
|
|
@ -500,7 +500,6 @@ export type DataInterface = {
|
||||||
removeAllSessions: () => Promise<void>;
|
removeAllSessions: () => Promise<void>;
|
||||||
getAllSessions: () => Promise<Array<SessionType>>;
|
getAllSessions: () => Promise<Array<SessionType>>;
|
||||||
|
|
||||||
eraseStorageServiceStateFromConversations: () => Promise<void>;
|
|
||||||
getConversationCount: () => Promise<number>;
|
getConversationCount: () => Promise<number>;
|
||||||
saveConversation: (data: ConversationType) => Promise<void>;
|
saveConversation: (data: ConversationType) => Promise<void>;
|
||||||
saveConversations: (array: Array<ConversationType>) => Promise<void>;
|
saveConversations: (array: Array<ConversationType>) => Promise<void>;
|
||||||
|
@ -794,6 +793,7 @@ export type DataInterface = {
|
||||||
|
|
||||||
removeAll: () => Promise<void>;
|
removeAll: () => Promise<void>;
|
||||||
removeAllConfiguration: (type?: RemoveAllConfiguration) => Promise<void>;
|
removeAllConfiguration: (type?: RemoveAllConfiguration) => Promise<void>;
|
||||||
|
eraseStorageServiceState: () => Promise<void>;
|
||||||
|
|
||||||
getMessagesNeedingUpgrade: (
|
getMessagesNeedingUpgrade: (
|
||||||
limit: number,
|
limit: number,
|
||||||
|
@ -933,6 +933,7 @@ export type ClientExclusiveInterface = {
|
||||||
|
|
||||||
updateConversation: (data: ConversationType) => void;
|
updateConversation: (data: ConversationType) => void;
|
||||||
removeConversation: (id: string) => Promise<void>;
|
removeConversation: (id: string) => Promise<void>;
|
||||||
|
flushUpdateConversationBatcher: () => Promise<void>;
|
||||||
|
|
||||||
searchMessages: ({
|
searchMessages: ({
|
||||||
query,
|
query,
|
||||||
|
|
|
@ -249,7 +249,6 @@ const dataInterface: ServerInterface = {
|
||||||
removeAllSessions,
|
removeAllSessions,
|
||||||
getAllSessions,
|
getAllSessions,
|
||||||
|
|
||||||
eraseStorageServiceStateFromConversations,
|
|
||||||
getConversationCount,
|
getConversationCount,
|
||||||
saveConversation,
|
saveConversation,
|
||||||
saveConversations,
|
saveConversations,
|
||||||
|
@ -384,6 +383,7 @@ const dataInterface: ServerInterface = {
|
||||||
|
|
||||||
removeAll,
|
removeAll,
|
||||||
removeAllConfiguration,
|
removeAllConfiguration,
|
||||||
|
eraseStorageServiceState,
|
||||||
|
|
||||||
getMessagesNeedingUpgrade,
|
getMessagesNeedingUpgrade,
|
||||||
getMessagesWithVisualMediaAttachments,
|
getMessagesWithVisualMediaAttachments,
|
||||||
|
@ -1621,18 +1621,6 @@ async function getConversationById(
|
||||||
return jsonToObject(row.json);
|
return jsonToObject(row.json);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function eraseStorageServiceStateFromConversations(): Promise<void> {
|
|
||||||
const db = getInstance();
|
|
||||||
|
|
||||||
db.prepare<EmptyQuery>(
|
|
||||||
`
|
|
||||||
UPDATE conversations
|
|
||||||
SET
|
|
||||||
json = json_remove(json, '$.storageID', '$.needsStorageServiceSync', '$.unknownFields', '$.storageProfileKey');
|
|
||||||
`
|
|
||||||
).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllConversationsSync(db = getInstance()): Array<ConversationType> {
|
function getAllConversationsSync(db = getInstance()): Array<ConversationType> {
|
||||||
const rows: ConversationRows = db
|
const rows: ConversationRows = db
|
||||||
.prepare<EmptyQuery>(
|
.prepare<EmptyQuery>(
|
||||||
|
@ -5583,6 +5571,40 @@ async function removeAllConfiguration(
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function eraseStorageServiceState(): Promise<void> {
|
||||||
|
const db = getInstance();
|
||||||
|
|
||||||
|
db.exec(`
|
||||||
|
-- Conversations
|
||||||
|
UPDATE conversations
|
||||||
|
SET
|
||||||
|
json = json_remove(json, '$.storageID', '$.needsStorageServiceSync', '$.storageUnknownFields');
|
||||||
|
|
||||||
|
-- Stickers
|
||||||
|
UPDATE sticker_packs
|
||||||
|
SET
|
||||||
|
storageID = null,
|
||||||
|
storageVersion = null,
|
||||||
|
storageUnknownFields = null,
|
||||||
|
storageNeedsSync = 0;
|
||||||
|
|
||||||
|
UPDATE uninstalled_sticker_packs
|
||||||
|
SET
|
||||||
|
storageID = null,
|
||||||
|
storageVersion = null,
|
||||||
|
storageUnknownFields = null,
|
||||||
|
storageNeedsSync = 0;
|
||||||
|
|
||||||
|
-- Story Distribution Lists
|
||||||
|
UPDATE storyDistributions
|
||||||
|
SET
|
||||||
|
storageID = null,
|
||||||
|
storageVersion = null,
|
||||||
|
storageUnknownFields = null,
|
||||||
|
storageNeedsSync = 0;
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_MESSAGE_MIGRATION_ATTEMPTS = 5;
|
const MAX_MESSAGE_MIGRATION_ATTEMPTS = 5;
|
||||||
|
|
||||||
async function getMessagesNeedingUpgrade(
|
async function getMessagesNeedingUpgrade(
|
||||||
|
|
|
@ -18,6 +18,8 @@ import {
|
||||||
import { storageServiceUploadJob } from '../../services/storage';
|
import { storageServiceUploadJob } from '../../services/storage';
|
||||||
import { sendStickerPackSync } from '../../shims/textsecure';
|
import { sendStickerPackSync } from '../../shims/textsecure';
|
||||||
import { trigger } from '../../shims/events';
|
import { trigger } from '../../shims/events';
|
||||||
|
import { ERASE_STORAGE_SERVICE } from './user';
|
||||||
|
import type { EraseStorageServiceStateAction } from './user';
|
||||||
|
|
||||||
import type { NoopActionType } from './noop';
|
import type { NoopActionType } from './noop';
|
||||||
|
|
||||||
|
@ -128,27 +130,27 @@ type UseStickerFulfilledAction = ReadonlyDeep<{
|
||||||
|
|
||||||
export type StickersActionType = ReadonlyDeep<
|
export type StickersActionType = ReadonlyDeep<
|
||||||
| ClearInstalledStickerPackAction
|
| ClearInstalledStickerPackAction
|
||||||
|
| InstallStickerPackFulfilledAction
|
||||||
|
| NoopActionType
|
||||||
| StickerAddedAction
|
| StickerAddedAction
|
||||||
| StickerPackAddedAction
|
| StickerPackAddedAction
|
||||||
| InstallStickerPackFulfilledAction
|
|
||||||
| UninstallStickerPackFulfilledAction
|
|
||||||
| StickerPackUpdatedAction
|
|
||||||
| StickerPackRemovedAction
|
| StickerPackRemovedAction
|
||||||
|
| StickerPackUpdatedAction
|
||||||
|
| UninstallStickerPackFulfilledAction
|
||||||
| UseStickerFulfilledAction
|
| UseStickerFulfilledAction
|
||||||
| NoopActionType
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// Action Creators
|
// Action Creators
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
downloadStickerPack,
|
|
||||||
clearInstalledStickerPack,
|
clearInstalledStickerPack,
|
||||||
|
downloadStickerPack,
|
||||||
|
installStickerPack,
|
||||||
removeStickerPack,
|
removeStickerPack,
|
||||||
stickerAdded,
|
stickerAdded,
|
||||||
stickerPackAdded,
|
stickerPackAdded,
|
||||||
installStickerPack,
|
|
||||||
uninstallStickerPack,
|
|
||||||
stickerPackUpdated,
|
stickerPackUpdated,
|
||||||
|
uninstallStickerPack,
|
||||||
useSticker,
|
useSticker,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,7 +358,7 @@ export function getEmptyState(): StickersStateType {
|
||||||
|
|
||||||
export function reducer(
|
export function reducer(
|
||||||
state: Readonly<StickersStateType> = getEmptyState(),
|
state: Readonly<StickersStateType> = getEmptyState(),
|
||||||
action: Readonly<StickersActionType>
|
action: Readonly<StickersActionType | EraseStorageServiceStateAction>
|
||||||
): StickersStateType {
|
): StickersStateType {
|
||||||
if (action.type === 'stickers/STICKER_PACK_ADDED') {
|
if (action.type === 'stickers/STICKER_PACK_ADDED') {
|
||||||
// ts complains due to `stickers: {}` being overridden by the payload
|
// ts complains due to `stickers: {}` being overridden by the payload
|
||||||
|
@ -497,5 +499,26 @@ export function reducer(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === ERASE_STORAGE_SERVICE) {
|
||||||
|
const { packs } = state;
|
||||||
|
|
||||||
|
const entries = Object.entries(packs).map(([id, pack]) => {
|
||||||
|
return [
|
||||||
|
id,
|
||||||
|
omit(pack, [
|
||||||
|
'storageID',
|
||||||
|
'storageVersion',
|
||||||
|
'storageUnknownFields',
|
||||||
|
'storageNeedsSync',
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
packs: Object.fromEntries(entries),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,15 +56,29 @@ type UserChangedActionType = ReadonlyDeep<{
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type UserActionType = ReadonlyDeep<UserChangedActionType>;
|
export const ERASE_STORAGE_SERVICE = 'user/ERASE_STORAGE_SERVICE_STATE';
|
||||||
|
export type EraseStorageServiceStateAction = ReadonlyDeep<{
|
||||||
|
type: typeof ERASE_STORAGE_SERVICE;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type UserActionType = ReadonlyDeep<
|
||||||
|
UserChangedActionType | EraseStorageServiceStateAction
|
||||||
|
>;
|
||||||
|
|
||||||
// Action Creators
|
// Action Creators
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
|
eraseStorageServiceState,
|
||||||
userChanged,
|
userChanged,
|
||||||
manualReconnect,
|
manualReconnect,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function eraseStorageServiceState(): EraseStorageServiceStateAction {
|
||||||
|
return {
|
||||||
|
type: ERASE_STORAGE_SERVICE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function userChanged(attributes: {
|
function userChanged(attributes: {
|
||||||
interactionMode?: 'mouse' | 'keyboard';
|
interactionMode?: 'mouse' | 'keyboard';
|
||||||
ourConversationId?: string;
|
ourConversationId?: string;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue