properly reject emoji file names for dropbox

This commit is contained in:
fyears 2022-04-16 11:36:04 +08:00
parent 9a4dd0186d
commit cb7a372b69
3 changed files with 97 additions and 14 deletions

View File

@ -68,6 +68,7 @@
"buffer": "^6.0.3", "buffer": "^6.0.3",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"dropbox": "^10.22.0", "dropbox": "^10.22.0",
"emoji-regex": "^10.1.0",
"http-status-codes": "^2.2.0", "http-status-codes": "^2.2.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View File

@ -3,6 +3,7 @@ import * as path from "path";
import { base32, base64url } from "rfc4648"; import { base32, base64url } from "rfc4648";
import XRegExp from "xregexp"; import XRegExp from "xregexp";
import emojiRegex from "emoji-regex";
import { log } from "./moreOnLog"; import { log } from "./moreOnLog";
@ -162,6 +163,34 @@ export const isVaildText = (a: string) => {
); );
}; };
/**
* Use regex to detect a text contains emoji or not.
* @param a
* @returns
*/
export const hasEmojiInText = (a: string) => {
const regex = emojiRegex();
return regex.test(a);
};
/**
* Convert the headers to a normal object.
* @param h
* @param toLower
* @returns
*/
export const headersToRecord = (h: Headers, toLower: boolean = true) => {
const res: Record<string, string> = {};
h.forEach((v, k) => {
if (toLower) {
res[k.toLowerCase()] = v;
} else {
res[k] = v;
}
});
return res;
};
/** /**
* If input is already a folder, returns it as is; * If input is already a folder, returns it as is;
* And if input is a file, returns its direname. * And if input is a file, returns its direname.

View File

@ -1,4 +1,4 @@
import { Dropbox, DropboxAuth, files } from "dropbox"; import { Dropbox, DropboxAuth, files, DropboxResponseError } from "dropbox";
import { Vault } from "obsidian"; import { Vault } from "obsidian";
import * as path from "path"; import * as path from "path";
import { import {
@ -8,7 +8,13 @@ import {
OAUTH2_FORCE_EXPIRE_MILLISECONDS, OAUTH2_FORCE_EXPIRE_MILLISECONDS,
} from "./baseTypes"; } from "./baseTypes";
import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt"; import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
import { bufferToArrayBuffer, getFolderLevels, mkdirpInVault } from "./misc"; import {
bufferToArrayBuffer,
getFolderLevels,
hasEmojiInText,
headersToRecord,
mkdirpInVault,
} from "./misc";
export { Dropbox } from "dropbox"; export { Dropbox } from "dropbox";
@ -338,6 +344,13 @@ export class WrappedDropboxClient {
} }
if (!this.vaultFolderExists) { if (!this.vaultFolderExists) {
log.info(`remote does not have folder /${this.remoteBaseDir}`); log.info(`remote does not have folder /${this.remoteBaseDir}`);
if (hasEmojiInText(`/${this.remoteBaseDir}`)) {
throw new Error(
`/${this.remoteBaseDir}: Error: Dropbox does not support emoji in folder names.`
);
}
await this.dropbox.filesCreateFolderV2({ await this.dropbox.filesCreateFolderV2({
path: `/${this.remoteBaseDir}`, path: `/${this.remoteBaseDir}`,
}); });
@ -423,6 +436,12 @@ export const uploadToRemote = async (
} }
uploadFile = getDropboxPath(uploadFile, client.remoteBaseDir); uploadFile = getDropboxPath(uploadFile, client.remoteBaseDir);
if (hasEmojiInText(uploadFile)) {
throw new Error(
`${uploadFile}: Error: Dropbox does not support emoji in file / folder names.`
);
}
const isFolder = fileOrFolderPath.endsWith("/"); const isFolder = fileOrFolderPath.endsWith("/");
if (isFolder && isRecursively) { if (isFolder && isRecursively) {
@ -442,7 +461,8 @@ export const uploadToRemote = async (
path: uploadFile, path: uploadFile,
}); });
foldersCreatedBefore?.add(uploadFile); foldersCreatedBefore?.add(uploadFile);
} catch (err) { } catch (e: unknown) {
const err = e as DropboxResponseError<files.CreateFolderError>;
if (err.status === 409) { if (err.status === 409) {
// pass // pass
foldersCreatedBefore?.add(uploadFile); foldersCreatedBefore?.add(uploadFile);
@ -455,10 +475,23 @@ export const uploadToRemote = async (
return res; return res;
} else { } else {
// if encrypted, upload a fake file with the encrypted file name // if encrypted, upload a fake file with the encrypted file name
try {
await client.dropbox.filesUpload({ await client.dropbox.filesUpload({
path: uploadFile, path: uploadFile,
contents: "", contents: "",
}); });
} catch (e: unknown) {
const err = e as DropboxResponseError<files.UploadError>;
// log.debug(
// `we are of error: ${JSON.stringify(
// headersToRecord(err.headers),
// null,
// 2
// )}, ${err.status}, ${JSON.stringify(err.error, null, 2)}`
// );
throw err;
}
return await getRemoteMeta(client, uploadFile); return await getRemoteMeta(client, uploadFile);
} }
} else { } else {
@ -480,6 +513,7 @@ export const uploadToRemote = async (
} }
// in dropbox, we don't need to create folders before uploading! cool! // in dropbox, we don't need to create folders before uploading! cool!
// TODO: filesUploadSession for larger files (>=150 MB) // TODO: filesUploadSession for larger files (>=150 MB)
try {
await client.dropbox.filesUpload({ await client.dropbox.filesUpload({
path: uploadFile, path: uploadFile,
contents: remoteContent, contents: remoteContent,
@ -487,6 +521,25 @@ export const uploadToRemote = async (
".tag": "overwrite", ".tag": "overwrite",
}, },
}); });
} catch (e: unknown) {
const err = e as DropboxResponseError<files.UploadError>;
// log.debug(
// `we are of error: ${JSON.stringify(
// headersToRecord(err.headers),
// null,
// 2
// )}, ${err.status}, ${JSON.stringify(err.error, null, 2)}`
// );
// if (err.status === 429) {
// // too many request
// } else if (err.status === 409) {
// // Endpoint Specific Error
// } else {
// throw err;
// }
throw err;
}
// we want to mark that parent folders are created // we want to mark that parent folders are created
if (foldersCreatedBefore !== undefined) { if (foldersCreatedBefore !== undefined) {
const dirs = getFolderLevels(uploadFile).map((x) => const dirs = getFolderLevels(uploadFile).map((x) =>