fix for encryption

This commit is contained in:
fyears 2024-02-25 15:14:28 +08:00
parent 40020c3e44
commit 1f4737bfb8
9 changed files with 182 additions and 107 deletions

View File

@ -169,8 +169,9 @@ export type DecisionTypeForMixedEntity =
* everything should be flat and primitive, so that we can copy. * everything should be flat and primitive, so that we can copy.
*/ */
export interface Entity { export interface Entity {
key: string; key?: string;
keyEnc: string; keyEnc?: string;
keyRaw: string;
mtimeCli?: number; mtimeCli?: number;
mtimeCliFmt?: string; mtimeCliFmt?: string;
mtimeSvr?: number; mtimeSvr?: number;
@ -178,7 +179,8 @@ export interface Entity {
prevSyncTime?: number; prevSyncTime?: number;
prevSyncTimeFmt?: string; prevSyncTimeFmt?: string;
size?: number; // might be unknown or to be filled size?: number; // might be unknown or to be filled
sizeEnc: number; sizeEnc?: number;
sizeRaw: number;
hash?: string; hash?: string;
etag?: string; etag?: string;
} }

View File

@ -32,20 +32,20 @@ export const getLocalEntityList = async (
); );
} }
r = { r = {
key: entry.path, key: entry.path, // local always unencrypted
keyEnc: entry.path, keyRaw: entry.path,
mtimeCli: mtimeLocal, mtimeCli: mtimeLocal,
mtimeSvr: mtimeLocal, mtimeSvr: mtimeLocal,
size: entry.stat.size, size: entry.stat.size, // local always unencrypted
sizeEnc: entry.stat.size, sizeRaw: entry.stat.size,
}; };
} else if (entry instanceof TFolder) { } else if (entry instanceof TFolder) {
key = `${entry.path}/`; key = `${entry.path}/`;
r = { r = {
key: key, key: key,
keyEnc: key, keyRaw: key,
size: 0, size: 0,
sizeEnc: 0, sizeRaw: 0,
}; };
} else { } else {
throw Error(`unexpected ${entry}`); throw Error(`unexpected ${entry}`);

View File

@ -240,6 +240,7 @@ export default class RemotelySavePlugin extends Plugin {
() => self.saveSettings() () => self.saveSettings()
); );
const remoteEntityList = await client.listAllFromRemote(); const remoteEntityList = await client.listAllFromRemote();
log.debug("remoteEntityList:");
log.debug(remoteEntityList); log.debug(remoteEntityList);
if (this.settings.currLogLevel === "info") { if (this.settings.currLogLevel === "info") {
@ -269,6 +270,7 @@ export default class RemotelySavePlugin extends Plugin {
this.app.vault.configDir, this.app.vault.configDir,
this.manifest.id this.manifest.id
); );
log.debug("localEntityList:");
log.debug(localEntityList); log.debug(localEntityList);
if (this.settings.currLogLevel === "info") { if (this.settings.currLogLevel === "info") {
@ -281,6 +283,7 @@ export default class RemotelySavePlugin extends Plugin {
this.db, this.db,
this.vaultRandomID this.vaultRandomID
); );
log.debug("prevSyncEntityList:");
log.debug(prevSyncEntityList); log.debug(prevSyncEntityList);
if (this.settings.currLogLevel === "info") { if (this.settings.currLogLevel === "info") {
@ -305,6 +308,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.skipSizeLargerThan ?? -1, this.settings.skipSizeLargerThan ?? -1,
this.settings.conflictAction ?? "keep_newer" this.settings.conflictAction ?? "keep_newer"
); );
log.info(`mixedEntityMappings:`);
log.info(mixedEntityMappings); // for debugging log.info(mixedEntityMappings); // for debugging
await insertSyncPlanRecordByVault( await insertSyncPlanRecordByVault(
this.db, this.db,

View File

@ -79,12 +79,12 @@ export const listFilesInObsFolder = async (
return { return {
itself: { itself: {
key: isFolder ? `${x}/` : x, key: isFolder ? `${x}/` : x, // local always unencrypted
keyEnc: isFolder ? `${x}/` : x, keyRaw: isFolder ? `${x}/` : x,
mtimeCli: statRes.mtime, mtimeCli: statRes.mtime,
mtimeSvr: statRes.mtime, mtimeSvr: statRes.mtime,
size: statRes.size, size: statRes.size, // local always unencrypted
sizeEnc: statRes.size, sizeRaw: statRes.size,
}, },
children: children, children: children,
}; };

View File

@ -83,22 +83,18 @@ const fromDropboxItemToEntity = (
if (x[".tag"] === "folder") { if (x[".tag"] === "folder") {
return { return {
key: key, keyRaw: key,
keyEnc: key, sizeRaw: 0,
size: 0,
sizeEnc: 0,
etag: `${x.id}\t`, etag: `${x.id}\t`,
} as Entity; } as Entity;
} else if (x[".tag"] === "file") { } else if (x[".tag"] === "file") {
const mtimeCli = Date.parse(x.client_modified).valueOf(); const mtimeCli = Date.parse(x.client_modified).valueOf();
const mtimeSvr = Date.parse(x.server_modified).valueOf(); const mtimeSvr = Date.parse(x.server_modified).valueOf();
return { return {
key: key, keyRaw: key,
keyEnc: key,
mtimeCli: mtimeCli, mtimeCli: mtimeCli,
mtimeSvr: mtimeSvr, mtimeSvr: mtimeSvr,
size: x.size, sizeRaw: x.size,
sizeEnc: x.size,
hash: x.content_hash, hash: x.content_hash,
etag: `${x.id}\t${x.content_hash}`, etag: `${x.id}\t${x.content_hash}`,
} as Entity; } as Entity;
@ -469,6 +465,11 @@ export const uploadToRemote = async (
let uploadFile = fileOrFolderPath; let uploadFile = fileOrFolderPath;
if (password !== "") { if (password !== "") {
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
throw Error(
`uploadToRemote(dropbox) you have password but remoteEncryptedKey is empty!`
);
}
uploadFile = remoteEncryptedKey; uploadFile = remoteEncryptedKey;
} }
uploadFile = getDropboxPath(uploadFile, client.remoteBaseDir); uploadFile = getDropboxPath(uploadFile, client.remoteBaseDir);

View File

@ -351,12 +351,10 @@ const fromDriveItemToEntity = (x: DriveItem, remoteBaseDir: string): Entity => {
const mtimeSvr = Date.parse(x?.fileSystemInfo!.lastModifiedDateTime!); const mtimeSvr = Date.parse(x?.fileSystemInfo!.lastModifiedDateTime!);
const mtimeCli = Date.parse(x?.fileSystemInfo!.lastModifiedDateTime!); const mtimeCli = Date.parse(x?.fileSystemInfo!.lastModifiedDateTime!);
return { return {
key: key, keyRaw: key,
keyEnc: key,
mtimeSvr: mtimeSvr, mtimeSvr: mtimeSvr,
mtimeCli: mtimeCli, mtimeCli: mtimeCli,
size: isFolder ? 0 : x.size!, sizeRaw: isFolder ? 0 : x.size!,
sizeEnc: isFolder ? 0 : x.size!,
// hash: ?? // TODO // hash: ?? // TODO
etag: x.cTag || "", // do NOT use x.eTag because it changes if meta changes etag: x.cTag || "", // do NOT use x.eTag because it changes if meta changes
}; };
@ -708,6 +706,11 @@ export const uploadToRemote = async (
let uploadFile = fileOrFolderPath; let uploadFile = fileOrFolderPath;
if (password !== "") { if (password !== "") {
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
throw Error(
`uploadToRemote(onedrive) you have password but remoteEncryptedKey is empty!`
);
}
uploadFile = remoteEncryptedKey; uploadFile = remoteEncryptedKey;
} }
uploadFile = getOnedrivePath(uploadFile, client.remoteBaseDir); uploadFile = getOnedrivePath(uploadFile, client.remoteBaseDir);

View File

@ -238,12 +238,10 @@ const fromS3ObjectToEntity = (
} }
const key = getLocalNoPrefixPath(x.Key!, remotePrefix); const key = getLocalNoPrefixPath(x.Key!, remotePrefix);
const r: Entity = { const r: Entity = {
key: key, keyRaw: key,
keyEnc: key,
mtimeSvr: mtimeSvr, mtimeSvr: mtimeSvr,
mtimeCli: mtimeCli, mtimeCli: mtimeCli,
size: x.Size!, sizeRaw: x.Size!,
sizeEnc: x.Size!,
etag: x.ETag, etag: x.ETag,
}; };
return r; return r;
@ -266,11 +264,21 @@ const fromS3HeadObjectToEntity = (
mtimeCli = m2; mtimeCli = m2;
} }
} }
// log.debug(
// `fromS3HeadObjectToEntity, fileOrFolderPathWithRemotePrefix=${fileOrFolderPathWithRemotePrefix}, remotePrefix=${remotePrefix}, x=${JSON.stringify(
// x
// )} `
// );
const key = getLocalNoPrefixPath(
fileOrFolderPathWithRemotePrefix,
remotePrefix
);
// log.debug(`fromS3HeadObjectToEntity, key=${key} after removing prefix`);
return { return {
key: getLocalNoPrefixPath(fileOrFolderPathWithRemotePrefix, remotePrefix), keyRaw: key,
mtimeSvr: mtimeSvr, mtimeSvr: mtimeSvr,
mtimeCli: mtimeCli, mtimeCli: mtimeCli,
size: x.ContentLength, sizeRaw: x.ContentLength,
etag: x.ETag, etag: x.ETag,
} as Entity; } as Entity;
}; };
@ -361,9 +369,15 @@ export const uploadToRemote = async (
log.debug(`uploading ${fileOrFolderPath}`); log.debug(`uploading ${fileOrFolderPath}`);
let uploadFile = fileOrFolderPath; let uploadFile = fileOrFolderPath;
if (password !== "") { if (password !== "") {
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
throw Error(
`uploadToRemote(s3) you have password but remoteEncryptedKey is empty!`
);
}
uploadFile = remoteEncryptedKey; uploadFile = remoteEncryptedKey;
} }
uploadFile = getRemoteWithPrefixPath(uploadFile, s3Config.remotePrefix ?? ""); uploadFile = getRemoteWithPrefixPath(uploadFile, s3Config.remotePrefix ?? "");
// log.debug(`actual uploadFile=${uploadFile}`);
const isFolder = fileOrFolderPath.endsWith("/"); const isFolder = fileOrFolderPath.endsWith("/");
if (isFolder && isRecursively) { if (isFolder && isRecursively) {
@ -459,9 +473,9 @@ export const uploadToRemote = async (
await upload.done(); await upload.done();
const res = await getRemoteMeta(s3Client, s3Config, uploadFile); const res = await getRemoteMeta(s3Client, s3Config, uploadFile);
log.debug( // log.debug(
`uploaded ${uploadFile} with res=${JSON.stringify(res, null, 2)}` // `uploaded ${uploadFile} with res=${JSON.stringify(res, null, 2)}`
); // );
return res; return res;
} }
}; };

View File

@ -212,12 +212,10 @@ const fromWebdavItemToEntity = (x: FileStat, remoteBaseDir: string) => {
} }
const mtimeSvr = Date.parse(x.lastmod).valueOf(); const mtimeSvr = Date.parse(x.lastmod).valueOf();
return { return {
key: key, keyRaw: key,
keyEnc: key,
mtimeSvr: mtimeSvr, mtimeSvr: mtimeSvr,
mtimeCli: mtimeSvr, // no universal way to set mtime in webdav mtimeCli: mtimeSvr, // no universal way to set mtime in webdav
size: x.size, sizeRaw: x.size,
sizeEnc: x.size,
etag: x.etag, etag: x.etag,
} as Entity; } as Entity;
}; };
@ -346,6 +344,11 @@ export const uploadToRemote = async (
await client.init(); await client.init();
let uploadFile = fileOrFolderPath; let uploadFile = fileOrFolderPath;
if (password !== "") { if (password !== "") {
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
throw Error(
`uploadToRemote(webdav) you have password but remoteEncryptedKey is empty!`
);
}
uploadFile = remoteEncryptedKey; uploadFile = remoteEncryptedKey;
} }
uploadFile = getWebdavPath(uploadFile, client.remoteBaseDir); uploadFile = getWebdavPath(uploadFile, client.remoteBaseDir);

View File

@ -75,7 +75,7 @@ export const isPasswordOk = async (
reason: "empty_remote", reason: "empty_remote",
}; };
} }
const santyCheckKey = remote[0].key; const santyCheckKey = remote[0].keyRaw;
if (santyCheckKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) { if (santyCheckKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) {
// this is encrypted using old base32! // this is encrypted using old base32!
// try to decrypt it using the provided password. // try to decrypt it using the provided password.
@ -161,6 +161,9 @@ const isSkipItemByName = (
configDir: string, configDir: string,
ignorePaths: string[] ignorePaths: string[]
) => { ) => {
if (key === undefined) {
throw Error(`isSkipItemByName meets undefinded key!`);
}
if (ignorePaths !== undefined && ignorePaths.length > 0) { if (ignorePaths !== undefined && ignorePaths.length > 0) {
for (const r of ignorePaths) { for (const r of ignorePaths) {
if (XRegExp(r, "A").test(key)) { if (XRegExp(r, "A").test(key)) {
@ -218,17 +221,25 @@ const copyEntityAndFixTimeFormat = (src: Entity) => {
*/ */
const decryptRemoteEntityInplace = async (remote: Entity, password: string) => { const decryptRemoteEntityInplace = async (remote: Entity, password: string) => {
if (password == undefined || password === "") { if (password == undefined || password === "") {
remote.key = remote.keyEnc; remote.key = remote.keyRaw;
remote.size = remote.sizeEnc; remote.keyEnc = remote.keyRaw;
remote.size = remote.sizeRaw;
remote.sizeEnc = remote.sizeRaw;
return remote; return remote;
} }
if (remote.keyEnc.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) { if (remote.keyRaw.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) {
remote.keyEnc = remote.keyRaw;
remote.key = await decryptBase32ToString(remote.keyEnc, password); remote.key = await decryptBase32ToString(remote.keyEnc, password);
} else if (remote.keyEnc.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE64URL)) { remote.sizeEnc = remote.sizeRaw;
} else if (remote.keyRaw.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE64URL)) {
remote.keyEnc = remote.keyRaw;
remote.key = await decryptBase64urlToString(remote.keyEnc, password); remote.key = await decryptBase64urlToString(remote.keyEnc, password);
remote.sizeEnc = remote.sizeRaw;
} else { } else {
throw Error(`unexpected key to decrypt=${remote.keyEnc}`); throw Error(
`unexpected key to decrypt: ${JSON.stringify(remote, null, 2)}`
);
} }
// TODO // TODO
@ -245,7 +256,7 @@ const decryptRemoteEntityInplace = async (remote: Entity, password: string) => {
*/ */
const ensureMTimeOfRemoteEntityValid = (remote: Entity) => { const ensureMTimeOfRemoteEntityValid = (remote: Entity) => {
if ( if (
!remote.key.endsWith("/") && !remote.key!.endsWith("/") &&
remote.mtimeCli === undefined && remote.mtimeCli === undefined &&
remote.mtimeSvr === undefined remote.mtimeSvr === undefined
) { ) {
@ -273,19 +284,36 @@ const encryptLocalEntityInplace = async (
password: string, password: string,
remoteKeyEnc: string | undefined remoteKeyEnc: string | undefined
) => { ) => {
if (password == undefined || password === "") { // log.debug(
local.sizeEnc = local.size!; // if no enc, the remote file has the same size // `encryptLocalEntityInplace: local=${JSON.stringify(
local.keyEnc = local.key; // local,
// null,
// 2
// )}, password=${
// password === undefined || password === "" ? "[empty]" : "[not empty]"
// }, remoteKeyEnc=${remoteKeyEnc}`
// );
if (local.key === undefined) {
// local.key should always have value
throw Error(`local ${local.keyRaw} is abnormal without key`);
}
if (password === undefined || password === "") {
local.sizeEnc = local.sizeRaw; // if no enc, the remote file has the same size
local.keyEnc = local.keyRaw;
return local; return local;
} }
// below is for having password // below is for having password
if (local.sizeEnc === undefined && local.size !== undefined) {
if (local.size === local.sizeEnc) { // it's not filled yet, we fill it
// size not transformed yet, we need to compute sizeEnc // local.size is possibly undefined if it's "prevSync" Entity
// but local.key should always have value
local.sizeEnc = getSizeFromOrigToEnc(local.size); local.sizeEnc = getSizeFromOrigToEnc(local.size);
} }
if (local.key === local.keyEnc) {
if (local.keyEnc === undefined || local.keyEnc === "") {
if ( if (
remoteKeyEnc !== undefined && remoteKeyEnc !== undefined &&
remoteKeyEnc !== "" && remoteKeyEnc !== "" &&
@ -328,7 +356,7 @@ export const ensembleMixedEnties = async (
) )
); );
const key = remoteCopied.key; const key = remoteCopied.key!;
if ( if (
isSkipItemByName( isSkipItemByName(
key, key,
@ -347,8 +375,17 @@ export const ensembleMixedEnties = async (
}; };
} }
if (Object.keys(finalMappings).length === 0 || localEntityList.length === 0) {
// Special checking:
// if one side is totally empty,
// usually that's a hard rest.
// So we need to ignore everything of prevSyncEntityList to avoid deletions!
// TODO: acutally erase everything of prevSyncEntityList?
// TODO: local should also go through a isSkipItemByName checking beforehand
} else {
// normally go through the prevSyncEntityList
for (const prevSync of prevSyncEntityList) { for (const prevSync of prevSyncEntityList) {
const key = prevSync.key; const key = prevSync.key!;
if ( if (
isSkipItemByName( isSkipItemByName(
key, key,
@ -380,12 +417,13 @@ export const ensembleMixedEnties = async (
}; };
} }
} }
}
// local has to be last // local has to be last
// because we want to get keyEnc based on the remote // because we want to get keyEnc based on the remote
// (we don't consume prevSync here because it gains no benefit) // (we don't consume prevSync here because it gains no benefit)
for (const local of localEntityList) { for (const local of localEntityList) {
const key = local.key; const key = local.key!;
if ( if (
isSkipItemByName( isSkipItemByName(
key, key,
@ -514,7 +552,7 @@ export const getSyncPlanInplace = async (
// If only one compares true (no prev also means it compares False), the other is modified. Backup and sync. // If only one compares true (no prev also means it compares False), the other is modified. Backup and sync.
if ( if (
skipSizeLargerThan <= 0 || skipSizeLargerThan <= 0 ||
remote.sizeEnc <= skipSizeLargerThan remote.sizeEnc! <= skipSizeLargerThan
) { ) {
mixedEntry.decisionBranch = 9; mixedEntry.decisionBranch = 9;
mixedEntry.decision = "modified_remote"; mixedEntry.decision = "modified_remote";
@ -530,7 +568,7 @@ export const getSyncPlanInplace = async (
// If only one compares true (no prev also means it compares False), the other is modified. Backup and sync. // If only one compares true (no prev also means it compares False), the other is modified. Backup and sync.
if ( if (
skipSizeLargerThan <= 0 || skipSizeLargerThan <= 0 ||
local.sizeEnc <= skipSizeLargerThan local.sizeEnc! <= skipSizeLargerThan
) { ) {
mixedEntry.decisionBranch = 10; mixedEntry.decisionBranch = 10;
mixedEntry.decision = "modified_local"; mixedEntry.decision = "modified_local";
@ -559,7 +597,7 @@ export const getSyncPlanInplace = async (
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
} }
} else if (conflictAction === "keep_larger") { } else if (conflictAction === "keep_larger") {
if (local.sizeEnc >= remote.sizeEnc) { if (local.sizeEnc! >= remote.sizeEnc!) {
mixedEntry.decisionBranch = 13; mixedEntry.decisionBranch = 13;
mixedEntry.decision = "conflict_created_keep_local"; mixedEntry.decision = "conflict_created_keep_local";
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
@ -588,7 +626,7 @@ export const getSyncPlanInplace = async (
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
} }
} else if (conflictAction === "keep_larger") { } else if (conflictAction === "keep_larger") {
if (local.sizeEnc >= remote.sizeEnc) { if (local.sizeEnc! >= remote.sizeEnc!) {
mixedEntry.decisionBranch = 18; mixedEntry.decisionBranch = 18;
mixedEntry.decision = "conflict_modified_keep_local"; mixedEntry.decision = "conflict_modified_keep_local";
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
@ -616,7 +654,10 @@ export const getSyncPlanInplace = async (
// A is missing // A is missing
if (prevSync === undefined) { if (prevSync === undefined) {
// if B is not in the previous list, B is new // if B is not in the previous list, B is new
if (skipSizeLargerThan <= 0 || remote.sizeEnc <= skipSizeLargerThan) { if (
skipSizeLargerThan <= 0 ||
remote.sizeEnc! <= skipSizeLargerThan
) {
mixedEntry.decisionBranch = 3; mixedEntry.decisionBranch = 3;
mixedEntry.decision = "created_remote"; mixedEntry.decision = "created_remote";
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
@ -637,7 +678,10 @@ export const getSyncPlanInplace = async (
mixedEntry.decision = "deleted_local"; mixedEntry.decision = "deleted_local";
} else { } else {
// if B is in the previous list and MODIFIED, B has been deleted by A but modified by B // if B is in the previous list and MODIFIED, B has been deleted by A but modified by B
if (skipSizeLargerThan <= 0 || remote.sizeEnc <= skipSizeLargerThan) { if (
skipSizeLargerThan <= 0 ||
remote.sizeEnc! <= skipSizeLargerThan
) {
mixedEntry.decisionBranch = 5; mixedEntry.decisionBranch = 5;
mixedEntry.decision = "modified_remote"; mixedEntry.decision = "modified_remote";
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
@ -654,7 +698,7 @@ export const getSyncPlanInplace = async (
if (prevSync === undefined) { if (prevSync === undefined) {
// if A is not in the previous list, A is new // if A is not in the previous list, A is new
if (skipSizeLargerThan <= 0 || local.sizeEnc <= skipSizeLargerThan) { if (skipSizeLargerThan <= 0 || local.sizeEnc! <= skipSizeLargerThan) {
mixedEntry.decisionBranch = 6; mixedEntry.decisionBranch = 6;
mixedEntry.decision = "created_local"; mixedEntry.decision = "created_local";
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
@ -675,7 +719,7 @@ export const getSyncPlanInplace = async (
mixedEntry.decision = "deleted_remote"; mixedEntry.decision = "deleted_remote";
} else { } else {
// if A is in the previous list and MODIFIED, A has been deleted by B but modified by A // if A is in the previous list and MODIFIED, A has been deleted by B but modified by A
if (skipSizeLargerThan <= 0 || local.sizeEnc <= skipSizeLargerThan) { if (skipSizeLargerThan <= 0 || local.sizeEnc! <= skipSizeLargerThan) {
mixedEntry.decisionBranch = 8; mixedEntry.decisionBranch = 8;
mixedEntry.decision = "modified_local"; mixedEntry.decision = "modified_local";
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
@ -744,9 +788,9 @@ const splitThreeStepsOnEntityMappings = (
val.decision === "folder_existed_remote" || val.decision === "folder_existed_remote" ||
val.decision === "folder_to_be_created" val.decision === "folder_to_be_created"
) { ) {
log.debug(`splitting folder: key=${key},val=${JSON.stringify(val)}`); // log.debug(`splitting folder: key=${key},val=${JSON.stringify(val)}`);
const level = atWhichLevel(key); const level = atWhichLevel(key);
log.debug(`atWhichLevel: ${level}`); // log.debug(`atWhichLevel: ${level}`);
const k = folderCreationOps[level - 1]; const k = folderCreationOps[level - 1];
if (k === undefined || k === null) { if (k === undefined || k === null) {
folderCreationOps[level - 1] = [val]; folderCreationOps[level - 1] = [val];
@ -818,13 +862,13 @@ const dispatchOperationToActualV3 = async (
localDeleteFunc: any, localDeleteFunc: any,
password: string password: string
) => { ) => {
log.debug( // log.debug(
`inside dispatchOperationToActualV3, key=${key}, r=${JSON.stringify( // `inside dispatchOperationToActualV3, key=${key}, r=${JSON.stringify(
r, // r,
null, // null,
2 // 2
)}` // )}`
); // );
if (r.decision === "only_history") { if (r.decision === "only_history") {
clearPrevSyncRecordByVault(db, vaultRandomID, key); clearPrevSyncRecordByVault(db, vaultRandomID, key);
} else if ( } else if (
@ -850,6 +894,7 @@ const dispatchOperationToActualV3 = async (
// special treatment for OneDrive: do nothing, skip empty file without encryption // special treatment for OneDrive: do nothing, skip empty file without encryption
// if it's empty folder, or it's encrypted file/folder, it continues to be uploaded. // if it's empty folder, or it's encrypted file/folder, it continues to be uploaded.
} else { } else {
// log.debug(`before upload in sync, r=${JSON.stringify(r, null, 2)}`);
const remoteObjMeta = await client.uploadToRemote( const remoteObjMeta = await client.uploadToRemote(
r.key, r.key,
vault, vault,
@ -857,6 +902,7 @@ const dispatchOperationToActualV3 = async (
password, password,
r.local!.keyEnc r.local!.keyEnc
); );
await decryptRemoteEntityInplace(remoteObjMeta, password);
await upsertPrevSyncRecordByVault(db, vaultRandomID, remoteObjMeta); await upsertPrevSyncRecordByVault(db, vaultRandomID, remoteObjMeta);
} }
} else if ( } else if (
@ -897,6 +943,8 @@ const dispatchOperationToActualV3 = async (
password, password,
r.local!.keyEnc r.local!.keyEnc
); );
// we need to decrypt the key!!!
await decryptRemoteEntityInplace(remoteObjMeta, password);
await upsertPrevSyncRecordByVault(db, vaultRandomID, remoteObjMeta); await upsertPrevSyncRecordByVault(db, vaultRandomID, remoteObjMeta);
} else if (r.decision === "folder_to_be_deleted") { } else if (r.decision === "folder_to_be_deleted") {
await localDeleteFunc(r.key); await localDeleteFunc(r.key);
@ -921,10 +969,10 @@ export const doActualSync = async (
log.debug(`concurrency === ${concurrency}`); log.debug(`concurrency === ${concurrency}`);
const { folderCreationOps, deletionOps, uploadDownloads, realTotalCount } = const { folderCreationOps, deletionOps, uploadDownloads, realTotalCount } =
splitThreeStepsOnEntityMappings(mixedEntityMappings); splitThreeStepsOnEntityMappings(mixedEntityMappings);
log.debug(`folderCreationOps: ${JSON.stringify(folderCreationOps)}`); // log.debug(`folderCreationOps: ${JSON.stringify(folderCreationOps)}`);
log.debug(`deletionOps: ${JSON.stringify(deletionOps)}`); // log.debug(`deletionOps: ${JSON.stringify(deletionOps)}`);
log.debug(`uploadDownloads: ${JSON.stringify(uploadDownloads)}`); // log.debug(`uploadDownloads: ${JSON.stringify(uploadDownloads)}`);
log.debug(`realTotalCount: ${JSON.stringify(realTotalCount)}`); // log.debug(`realTotalCount: ${JSON.stringify(realTotalCount)}`);
const nested = [folderCreationOps, deletionOps, uploadDownloads]; const nested = [folderCreationOps, deletionOps, uploadDownloads];
const logTexts = [ const logTexts = [
@ -938,11 +986,11 @@ export const doActualSync = async (
log.debug(logTexts[i]); log.debug(logTexts[i]);
const operations = nested[i]; const operations = nested[i];
log.debug(`curr operations=${JSON.stringify(operations, null, 2)}`); // log.debug(`curr operations=${JSON.stringify(operations, null, 2)}`);
for (let j = 0; j < operations.length; ++j) { for (let j = 0; j < operations.length; ++j) {
const singleLevelOps = operations[j]; const singleLevelOps = operations[j];
log.debug(`singleLevelOps=${singleLevelOps}`); log.debug(`singleLevelOps=${JSON.stringify(singleLevelOps, null, 2)}`);
if (singleLevelOps === undefined || singleLevelOps === null) { if (singleLevelOps === undefined || singleLevelOps === null) {
continue; continue;
} }