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 {
|
||||
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
|
||||
// for all the conversation models in memory.
|
||||
// First, make changes to conversations in memory
|
||||
window.getConversations().forEach(conversation => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete conversation.attributes.senderKeyInfo;
|
||||
conversation.unset('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
|
||||
// the conversation list, instead of showing just the QR code screen.
|
||||
if (previousNumberId !== undefined) {
|
||||
|
@ -3102,10 +3108,17 @@ export async function startApp(): Promise<void> {
|
|||
log.info(
|
||||
'onKeysSync: updated storage service key, erasing state and fetching'
|
||||
);
|
||||
try {
|
||||
await window.storage.put('storageKey', storageServiceKeyBase64);
|
||||
await StorageService.eraseAllStorageServiceState({
|
||||
keepUnknownFields: true,
|
||||
});
|
||||
} catch (error) {
|
||||
log.info(
|
||||
'onKeysSync: Failed to erase storage service data, starting sync job anyway',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await StorageService.runStorageServiceSyncJob();
|
||||
|
|
|
@ -69,7 +69,9 @@ import { isSignalConversation } from '../util/isSignalConversation';
|
|||
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
|
||||
|
||||
const {
|
||||
eraseStorageServiceStateFromConversations,
|
||||
eraseStorageServiceState,
|
||||
flushUpdateConversationBatcher,
|
||||
getItemById,
|
||||
updateConversation,
|
||||
updateConversations,
|
||||
} = dataInterface;
|
||||
|
@ -1893,12 +1895,12 @@ export function enableStorageService(): void {
|
|||
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({
|
||||
keepUnknownFields = false,
|
||||
}: { keepUnknownFields?: boolean } = {}): Promise<void> {
|
||||
log.info('storageService.eraseAllStorageServiceState: starting...');
|
||||
|
||||
// First, update high-level storage service metadata
|
||||
await Promise.all([
|
||||
window.storage.remove('manifestVersion'),
|
||||
keepUnknownFields
|
||||
|
@ -1906,7 +1908,34 @@ export async function eraseAllStorageServiceState({
|
|||
: window.storage.remove('storage-service-unknown-records'),
|
||||
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');
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ const exclusiveInterface: ClientExclusiveInterface = {
|
|||
|
||||
// Client-side only
|
||||
|
||||
flushUpdateConversationBatcher,
|
||||
|
||||
shutdown,
|
||||
removeAllMessagesInConversation,
|
||||
|
||||
|
@ -458,6 +460,9 @@ const updateConversationBatcher = createBatcher<ConversationType>({
|
|||
function updateConversation(data: ConversationType): void {
|
||||
updateConversationBatcher.add(data);
|
||||
}
|
||||
async function flushUpdateConversationBatcher(): Promise<void> {
|
||||
await updateConversationBatcher.flushAndWait();
|
||||
}
|
||||
|
||||
async function updateConversations(
|
||||
array: Array<ConversationType>
|
||||
|
|
|
@ -500,7 +500,6 @@ export type DataInterface = {
|
|||
removeAllSessions: () => Promise<void>;
|
||||
getAllSessions: () => Promise<Array<SessionType>>;
|
||||
|
||||
eraseStorageServiceStateFromConversations: () => Promise<void>;
|
||||
getConversationCount: () => Promise<number>;
|
||||
saveConversation: (data: ConversationType) => Promise<void>;
|
||||
saveConversations: (array: Array<ConversationType>) => Promise<void>;
|
||||
|
@ -794,6 +793,7 @@ export type DataInterface = {
|
|||
|
||||
removeAll: () => Promise<void>;
|
||||
removeAllConfiguration: (type?: RemoveAllConfiguration) => Promise<void>;
|
||||
eraseStorageServiceState: () => Promise<void>;
|
||||
|
||||
getMessagesNeedingUpgrade: (
|
||||
limit: number,
|
||||
|
@ -933,6 +933,7 @@ export type ClientExclusiveInterface = {
|
|||
|
||||
updateConversation: (data: ConversationType) => void;
|
||||
removeConversation: (id: string) => Promise<void>;
|
||||
flushUpdateConversationBatcher: () => Promise<void>;
|
||||
|
||||
searchMessages: ({
|
||||
query,
|
||||
|
|
|
@ -249,7 +249,6 @@ const dataInterface: ServerInterface = {
|
|||
removeAllSessions,
|
||||
getAllSessions,
|
||||
|
||||
eraseStorageServiceStateFromConversations,
|
||||
getConversationCount,
|
||||
saveConversation,
|
||||
saveConversations,
|
||||
|
@ -384,6 +383,7 @@ const dataInterface: ServerInterface = {
|
|||
|
||||
removeAll,
|
||||
removeAllConfiguration,
|
||||
eraseStorageServiceState,
|
||||
|
||||
getMessagesNeedingUpgrade,
|
||||
getMessagesWithVisualMediaAttachments,
|
||||
|
@ -1621,18 +1621,6 @@ async function getConversationById(
|
|||
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> {
|
||||
const rows: ConversationRows = db
|
||||
.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;
|
||||
|
||||
async function getMessagesNeedingUpgrade(
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
import { storageServiceUploadJob } from '../../services/storage';
|
||||
import { sendStickerPackSync } from '../../shims/textsecure';
|
||||
import { trigger } from '../../shims/events';
|
||||
import { ERASE_STORAGE_SERVICE } from './user';
|
||||
import type { EraseStorageServiceStateAction } from './user';
|
||||
|
||||
import type { NoopActionType } from './noop';
|
||||
|
||||
|
@ -128,27 +130,27 @@ type UseStickerFulfilledAction = ReadonlyDeep<{
|
|||
|
||||
export type StickersActionType = ReadonlyDeep<
|
||||
| ClearInstalledStickerPackAction
|
||||
| InstallStickerPackFulfilledAction
|
||||
| NoopActionType
|
||||
| StickerAddedAction
|
||||
| StickerPackAddedAction
|
||||
| InstallStickerPackFulfilledAction
|
||||
| UninstallStickerPackFulfilledAction
|
||||
| StickerPackUpdatedAction
|
||||
| StickerPackRemovedAction
|
||||
| StickerPackUpdatedAction
|
||||
| UninstallStickerPackFulfilledAction
|
||||
| UseStickerFulfilledAction
|
||||
| NoopActionType
|
||||
>;
|
||||
|
||||
// Action Creators
|
||||
|
||||
export const actions = {
|
||||
downloadStickerPack,
|
||||
clearInstalledStickerPack,
|
||||
downloadStickerPack,
|
||||
installStickerPack,
|
||||
removeStickerPack,
|
||||
stickerAdded,
|
||||
stickerPackAdded,
|
||||
installStickerPack,
|
||||
uninstallStickerPack,
|
||||
stickerPackUpdated,
|
||||
uninstallStickerPack,
|
||||
useSticker,
|
||||
};
|
||||
|
||||
|
@ -356,7 +358,7 @@ export function getEmptyState(): StickersStateType {
|
|||
|
||||
export function reducer(
|
||||
state: Readonly<StickersStateType> = getEmptyState(),
|
||||
action: Readonly<StickersActionType>
|
||||
action: Readonly<StickersActionType | EraseStorageServiceStateAction>
|
||||
): StickersStateType {
|
||||
if (action.type === 'stickers/STICKER_PACK_ADDED') {
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
export const actions = {
|
||||
eraseStorageServiceState,
|
||||
userChanged,
|
||||
manualReconnect,
|
||||
};
|
||||
|
||||
function eraseStorageServiceState(): EraseStorageServiceStateAction {
|
||||
return {
|
||||
type: ERASE_STORAGE_SERVICE,
|
||||
};
|
||||
}
|
||||
|
||||
function userChanged(attributes: {
|
||||
interactionMode?: 'mouse' | 'keyboard';
|
||||
ourConversationId?: string;
|
||||
|
|
Loading…
Reference in a new issue