Fix MM-46309 (#6567)

* Fix MM-46309

* Address feedback

* Update dbPath and rename server databas instead of deleting

* ios fixes

Co-authored-by: Daniel Espino <danielespino@MacBook-Pro-de-Daniel.local>
This commit is contained in:
Daniel Espino García
2022-08-15 16:55:54 +02:00
committed by GitHub
parent 63e6290c76
commit ea595f1ced
7 changed files with 152 additions and 21 deletions

View File

@@ -48,6 +48,9 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
// Load data from other servers
syncOtherServers(serverUrl);
}
verifyPushProxy(serverUrl);
return result;
}
@@ -95,8 +98,6 @@ async function restAppEntry(serverUrl: string, since = 0, isUpgrade = false) {
const {config, license} = await getCommonSystemValues(database);
await deferredAppEntryActions(serverUrl, lastDisconnectedAt, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, initialTeamId, switchToChannel ? initialChannelId : undefined);
verifyPushProxy(serverUrl);
return {userId: currentUserId};
}

View File

@@ -23,7 +23,7 @@ import {schema as appSchema} from '@database/schema/app';
import {serverSchema} from '@database/schema/server';
import {queryActiveServer, queryServer, queryServerByIdentifier} from '@queries/app/servers';
import {deleteIOSDatabase} from '@utils/mattermost_managed';
import {hashCode} from '@utils/security';
import {urlSafeBase64Encode} from '@utils/security';
import {removeProtocol} from '@utils/url';
import type {AppDatabase, CreateServerDatabaseArgs, Models, RegisterServerDatabaseArgs, ServerDatabase, ServerDatabases} from '@typings/database/database';
@@ -132,7 +132,7 @@ class DatabaseManager {
private initServerDatabase = async (serverUrl: string): Promise<void> => {
await this.createServerDatabase({
config: {
dbName: hashCode(serverUrl),
dbName: urlSafeBase64Encode(serverUrl),
dbType: DatabaseType.SERVER,
serverUrl,
},
@@ -306,7 +306,7 @@ class DatabaseManager {
};
private deleteServerDatabaseFiles = async (serverUrl: string): Promise<void> => {
const databaseName = hashCode(serverUrl);
const databaseName = urlSafeBase64Encode(serverUrl);
if (Platform.OS === 'ios') {
// On iOS, we'll delete the *.db file under the shared app-group/databases folder

View File

@@ -23,10 +23,11 @@ import ServerDataOperator from '@database/operator/server_data_operator';
import {schema as appSchema} from '@database/schema/app';
import {serverSchema} from '@database/schema/server';
import {queryActiveServer, queryServer, queryServerByIdentifier} from '@queries/app/servers';
import {deleteLegacyFileCache} from '@utils/file';
import {emptyFunction} from '@utils/general';
import {logDebug, logError} from '@utils/log';
import {deleteIOSDatabase, getIOSAppGroupDetails} from '@utils/mattermost_managed';
import {hashCode} from '@utils/security';
import {deleteIOSDatabase, getIOSAppGroupDetails, renameIOSDatabase} from '@utils/mattermost_managed';
import {hashCode_DEPRECATED, urlSafeBase64Encode} from '@utils/security';
import {removeProtocol} from '@utils/url';
import type {AppDatabase, CreateServerDatabaseArgs, RegisterServerDatabaseArgs, Models, ServerDatabase, ServerDatabases} from '@typings/database/database';
@@ -126,7 +127,13 @@ class DatabaseManager {
if (serverUrl) {
try {
const databaseName = hashCode(serverUrl);
const databaseName = urlSafeBase64Encode(serverUrl);
const oldDatabaseName = hashCode_DEPRECATED(serverUrl);
// Remove any legacy database we may already have.
await this.renameDatabase(oldDatabaseName, databaseName);
deleteLegacyFileCache(serverUrl);
const databaseFilePath = this.getDatabaseFilePath(databaseName);
const migrations = ServerDatabaseMigrations;
const modelClasses = this.serverModels;
@@ -192,9 +199,9 @@ class DatabaseManager {
try {
const appDatabase = this.appDatabase?.database;
if (appDatabase) {
const isServerPresent = await this.isServerPresent(serverUrl);
const serverModel = await queryServer(appDatabase, serverUrl);
if (!isServerPresent) {
if (!serverModel) {
await appDatabase.write(async () => {
const serversCollection = appDatabase.collections.get(SERVERS);
await serversCollection.create((server: ServersModel) => {
@@ -205,6 +212,12 @@ class DatabaseManager {
server.lastActiveAt = 0;
});
});
} else if (serverModel.dbPath !== databaseFilePath) {
await appDatabase.write(async () => {
await serverModel.update((s) => {
s.dbPath = databaseFilePath;
});
});
} else if (identifier) {
await this.updateServerIdentifier(serverUrl, identifier, displayName);
}
@@ -412,8 +425,16 @@ class DatabaseManager {
* @returns {Promise<void>}
*/
private deleteServerDatabaseFiles = async (serverUrl: string): Promise<void> => {
const databaseName = hashCode(serverUrl);
const databaseName = urlSafeBase64Encode(serverUrl);
this.deleteServerDatabaseFilesByName(databaseName);
};
/**
* deleteServerDatabaseFilesByName: Removes the *.db file from the App-Group directory for iOS or the files directory for Android, given the database name
* @param {string} databaseName
* @returns {Promise<void>}
*/
private deleteServerDatabaseFilesByName = async (databaseName: string): Promise<void> => {
if (Platform.OS === 'ios') {
// On iOS, we'll delete the *.db file under the shared app-group/databases folder
deleteIOSDatabase({databaseName});
@@ -431,6 +452,47 @@ class DatabaseManager {
FileSystem.unlink(databaseWal).catch(emptyFunction);
};
/**
* deleteServerDatabaseFilesByName: Removes the *.db file from the App-Group directory for iOS or the files directory for Android, given the database name
* @param {string} databaseName
* @returns {Promise<void>}
*/
private renameDatabase = async (databaseName: string, newDBName: string): Promise<void> => {
if (Platform.OS === 'ios') {
// On iOS, we'll move the *.db file under the shared app-group/databases folder
renameIOSDatabase(databaseName, newDBName);
return;
}
// On Android, we'll move the *.db, the *.db-shm and *.db-wal files
const androidFilesDir = this.databaseDirectory;
const databaseFile = `${androidFilesDir}${databaseName}.db`;
const databaseShm = `${androidFilesDir}${databaseName}.db-shm`;
const databaseWal = `${androidFilesDir}${databaseName}.db-wal`;
const newDatabaseFile = `${androidFilesDir}${newDBName}.db`;
const newDatabaseShm = `${androidFilesDir}${newDBName}.db-shm`;
const newDatabaseWal = `${androidFilesDir}${newDBName}.db-wal`;
if (await FileSystem.exists(newDatabaseFile)) {
// Already renamed, do not try
return;
}
if (!await FileSystem.exists(databaseFile)) {
// Nothing to rename, do not try
return;
}
try {
await FileSystem.moveFile(databaseFile, newDatabaseFile);
await FileSystem.moveFile(databaseShm, newDatabaseShm);
await FileSystem.moveFile(databaseWal, newDatabaseWal);
} catch (error) {
// Do nothing
}
};
/**
* factoryReset: Removes the databases directory and all its contents on the respective platform
* @param {boolean} shouldRemoveDirectory
@@ -497,7 +559,7 @@ class DatabaseManager {
* @returns {string}
*/
private getDatabaseFilePath = (dbName: string): string => {
return Platform.OS === 'ios' ? `${this.databaseDirectory}/${dbName}.db` : `${this.databaseDirectory}/${dbName}.db`;
return Platform.OS === 'ios' ? `${this.databaseDirectory}/${dbName}.db` : `${this.databaseDirectory}${dbName}.db`;
};
/**

View File

@@ -18,7 +18,7 @@ import {generateId} from '@utils/general';
import keyMirror from '@utils/key_mirror';
import {logError} from '@utils/log';
import {deleteEntititesFile, getIOSAppGroupDetails} from '@utils/mattermost_managed';
import {hashCode} from '@utils/security';
import {hashCode_DEPRECATED, urlSafeBase64Encode} from '@utils/security';
import type FileModel from '@typings/database/models/servers/file';
@@ -165,8 +165,17 @@ export async function deleteV1Data() {
}
export async function deleteFileCache(serverUrl: string) {
const serverDir = hashCode(serverUrl);
const cacheDir = `${FileSystem.CachesDirectoryPath}/${serverDir}`;
const serverDir = urlSafeBase64Encode(serverUrl);
deleteFileCacheByDir(serverDir);
}
export async function deleteLegacyFileCache(serverUrl: string) {
const serverDir = hashCode_DEPRECATED(serverUrl);
deleteFileCacheByDir(serverDir);
}
async function deleteFileCacheByDir(dir: string) {
const cacheDir = `${FileSystem.CachesDirectoryPath}/${dir}`;
if (cacheDir) {
const cacheDirInfo = await FileSystem.exists(cacheDir);
if (cacheDirInfo) {
@@ -340,9 +349,10 @@ export function getFileType(file: FileInfo): string {
}
export function getLocalFilePathFromFile(serverUrl: string, file: FileInfo | FileModel) {
const fileIdPath = file.id?.replace(/[^0-9a-z]/g, '');
if (serverUrl) {
const server = hashCode(serverUrl);
if (file?.name) {
const server = urlSafeBase64Encode(serverUrl);
if (file?.name && !file.name.includes('/')) {
let extension: string | undefined = file.extension;
let filename = file.name;
@@ -362,9 +372,9 @@ export function getLocalFilePathFromFile(serverUrl: string, file: FileInfo | Fil
}
}
return `${FileSystem.CachesDirectoryPath}/${server}/${filename}-${hashCode(file.id!)}.${extension}`;
return `${FileSystem.CachesDirectoryPath}/${server}/${filename}-${fileIdPath}.${extension}`;
} else if (file?.id && file?.extension) {
return `${FileSystem.CachesDirectoryPath}/${server}/${file.id}.${file.extension}`;
return `${FileSystem.CachesDirectoryPath}/${server}/${fileIdPath}.${file.extension}`;
}
}
@@ -504,8 +514,9 @@ export const getAllFilesInCachesDirectory = async (serverUrl: string) => {
try {
const files: FileSystem.ReadDirItem[][] = [];
const directoryFiles = await FileSystem.readDir(`${FileSystem.CachesDirectoryPath}/${hashCode(serverUrl)}`);
const directoryFiles = await FileSystem.readDir(`${FileSystem.CachesDirectoryPath}/${urlSafeBase64Encode(serverUrl)}`);
files.push(directoryFiles);
const flattenedFiles = files.flat();
const totalSize = flattenedFiles.reduce((acc, file) => acc + file.size, 0);
return {

View File

@@ -44,6 +44,15 @@ export const deleteIOSDatabase = ({
MattermostManaged.deleteDatabaseDirectory(databaseName, shouldRemoveDirectory, () => null);
};
/**
* renameIOSDatabase renames the .db and any other related file to the new name.
* @param {string} from original database name
* @param {string} to new database name
*/
export const renameIOSDatabase = (from: string, to: string) => {
MattermostManaged.renameDatabase(from, to, () => null);
};
export const deleteEntititesFile = (callback?: (success: boolean) => void) => {
if (Platform.OS === 'ios') {
MattermostManaged.deleteEntititesFile((result: boolean) => {

View File

@@ -2,13 +2,15 @@
// See LICENSE.txt for license information.
import CookieManager from '@react-native-cookies/cookies';
import base64 from 'base-64';
export async function getCSRFFromCookie(url: string) {
const cookies = await CookieManager.get(url, false);
return cookies.MMCSRF?.value;
}
export const hashCode = (str: string): string => {
// This has been deprecated and is only used for migrations
export const hashCode_DEPRECATED = (str: string): string => {
let hash = 0;
let i;
let chr;
@@ -23,3 +25,7 @@ export const hashCode = (str: string): string => {
}
return hash.toString();
};
export const urlSafeBase64Encode = (str: string): string => {
return base64.encode(str).replace(/\+/g, '-').replace(/\//g, '_');
};

View File

@@ -95,6 +95,48 @@ RCT_EXPORT_METHOD(deleteDatabaseDirectory: (NSString *)databaseName shouldRemov
}
}
RCT_EXPORT_METHOD(renameDatabase: (NSString *)databaseName to: (NSString *) newDBName callback: (RCTResponseSenderBlock)callback){
@try {
NSDictionary *appGroupDir = [self appGroupSharedDirectory];
NSString *databaseDir;
NSString *newDBDir;
if(databaseName){
databaseDir = [NSString stringWithFormat:@"%@/%@%@", appGroupDir[@"databasePath"], databaseName , @".db"];
}
if (newDBName){
newDBDir = [NSString stringWithFormat:@"%@/%@%@", appGroupDir[@"databasePath"], newDBName , @".db"];
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
BOOL destinationHasFile = [fileManager fileExistsAtPath:newDBDir];
if (!destinationHasFile && [fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@-wal", databaseDir]]) {
[fileManager moveItemAtPath:[NSString stringWithFormat:@"%@-wal", databaseDir] toPath:[NSString stringWithFormat:@"%@-wal", newDBDir] error:nil];
}
if (!destinationHasFile && [fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@-shm", databaseDir]]) {
[fileManager moveItemAtPath:[NSString stringWithFormat:@"%@-shm", databaseDir] toPath:[NSString stringWithFormat:@"%@-shm", newDBDir] error:nil];
}
BOOL successCode = destinationHasFile;
if (!destinationHasFile && [fileManager fileExistsAtPath:databaseDir]){
successCode = [fileManager moveItemAtPath:databaseDir toPath: newDBDir error:&error];
}
NSNumber *success= [NSNumber numberWithBool:successCode];
callback(@[(error ?: [NSNull null]), success]);
}
@catch (NSException *exception) {
NSLog(@"%@", exception.reason);
callback(@[exception.reason, @NO]);
}
}
RCT_EXPORT_METHOD(deleteEntititesFile: (RCTResponseSenderBlock) callback) {
@try {
NSDictionary *appGroupDir = [self appGroupSharedDirectory];