Support symlinked stickers.noindex folder

This commit is contained in:
Fedor Indutny
2025-09-08 12:44:44 -07:00
committed by GitHub
parent 87704409c3
commit ebdf651dca
6 changed files with 61 additions and 35 deletions

View File

@@ -5469,7 +5469,7 @@ export class ConversationModel {
async #getTemporaryAvatarPath(): Promise<string | undefined> {
const {
copyIntoTempDirectory,
copyAttachmentIntoTempDirectory,
deleteAttachmentData,
getAbsoluteAttachmentPath,
getAbsoluteTempPath,
@@ -5505,7 +5505,7 @@ export class ConversationModel {
});
try {
const { path: tempPath } = await copyIntoTempDirectory(
const { path: tempPath } = await copyAttachmentIntoTempDirectory(
getAbsoluteAttachmentPath(plaintextPath)
);
return getAbsoluteTempPath(tempPath);

View File

@@ -64,10 +64,10 @@ type EncryptedWriter = (data: Uint8Array) => Promise<LocalAttachmentV2Type>;
type MigrationsModuleType = {
attachmentsPath: string;
copyIntoAttachmentsDirectory: (
copyStickerIntoAttachmentsDirectory: (
path: string
) => Promise<{ path: string; size: number }>;
copyIntoTempDirectory: (
copyAttachmentIntoTempDirectory: (
path: string
) => Promise<{ path: string; size: number }>;
deleteAttachmentData: (path: string) => Promise<void>;
@@ -241,8 +241,6 @@ export function initializeMigrations({
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
const deleteOnDisk = Attachments.createDeleter(attachmentsPath);
const writeNewAttachmentData = createEncryptedWriterForNew(attachmentsPath);
const copyIntoAttachmentsDirectory =
Attachments.copyIntoAttachmentsDirectory(attachmentsPath);
const doesAttachmentExist = createDoesExist(attachmentsPath);
const stickersPath = getStickersPath(userDataPath);
@@ -250,6 +248,11 @@ export function initializeMigrations({
const writeNewStickerData = createEncryptedWriterForNew(stickersPath);
const deleteSticker = Attachments.createDeleter(stickersPath);
const readStickerData = createEncryptedReader(stickersPath);
const copyStickerIntoAttachmentsDirectory =
Attachments.copyIntoAttachmentsDirectory({
sourceDir: stickersPath,
targetDir: attachmentsPath,
});
const badgesPath = getBadgesPath(userDataPath);
const getAbsoluteBadgeImageFilePath = createAbsolutePathGetter(badgesPath);
@@ -261,8 +264,11 @@ export function initializeMigrations({
const writeNewPlaintextTempData = createWriterForNew(tempPath);
const deleteTempFile = Attachments.createDeleter(tempPath);
const readTempData = createEncryptedReader(tempPath);
const copyIntoTempDirectory =
Attachments.copyIntoAttachmentsDirectory(tempPath);
const copyAttachmentIntoTempDirectory =
Attachments.copyIntoAttachmentsDirectory({
sourceDir: attachmentsPath,
targetDir: tempPath,
});
const draftPath = getDraftPath(userDataPath);
const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath);
@@ -282,8 +288,8 @@ export function initializeMigrations({
return {
attachmentsPath,
copyIntoAttachmentsDirectory,
copyIntoTempDirectory,
copyStickerIntoAttachmentsDirectory,
copyAttachmentIntoTempDirectory,
deleteAttachmentData: deleteOnDisk,
deleteAvatar,
deleteDownloadData: deleteDownloadOnDisk,
@@ -390,9 +396,10 @@ type AttachmentsModuleType = {
root: string
) => (relativePath: string) => Promise<Uint8Array>;
copyIntoAttachmentsDirectory: (
root: string
) => (sourcePath: string) => Promise<{ path: string; size: number }>;
copyIntoAttachmentsDirectory: (options: {
sourceDir: string;
targetDir: string;
}) => (sourcePath: string) => Promise<{ path: string; size: number }>;
createWriterForNew: (
root: string,

View File

@@ -195,11 +195,12 @@ function showLightboxForViewOnceMedia(
);
}
const { copyIntoTempDirectory, getAbsoluteAttachmentPath } =
const { copyAttachmentIntoTempDirectory, getAbsoluteAttachmentPath } =
window.Signal.Migrations;
const absolutePath = getAbsoluteAttachmentPath(firstAttachment.path);
const { path: tempPath } = await copyIntoTempDirectory(absolutePath);
const { path: tempPath } =
await copyAttachmentIntoTempDirectory(absolutePath);
const tempAttachment = {
...firstAttachment,
path: tempPath,

View File

@@ -55,24 +55,32 @@ describe('Attachments', () => {
it('throws if passed a non-string', () => {
assert.throws(() => {
Attachments.copyIntoAttachmentsDirectory(1234 as unknown as string);
Attachments.copyIntoAttachmentsDirectory({
targetDir: 1234 as unknown as string,
sourceDir: 1234 as unknown as string,
});
}, TypeError);
assert.throws(() => {
Attachments.copyIntoAttachmentsDirectory(null as unknown as string);
Attachments.copyIntoAttachmentsDirectory({
targetDir: null as unknown as string,
sourceDir: null as unknown as string,
});
}, TypeError);
});
it('returns a function that rejects if the source path is not a string', async () => {
const copier = Attachments.copyIntoAttachmentsDirectory(
await getFakeAttachmentsDirectory()
);
const copier = Attachments.copyIntoAttachmentsDirectory({
sourceDir: await getFakeAttachmentsDirectory(),
targetDir: await getFakeAttachmentsDirectory(),
});
await assert.isRejected(copier(123 as unknown as string));
});
it('returns a function that rejects if the source path is not in the user config directory', async () => {
const copier = Attachments.copyIntoAttachmentsDirectory(
await getFakeAttachmentsDirectory()
);
const copier = Attachments.copyIntoAttachmentsDirectory({
sourceDir: await getFakeAttachmentsDirectory(),
targetDir: await getFakeAttachmentsDirectory(),
});
await assert.isRejected(
copier(path.join(tempRootDirectory, 'hello.txt')),
"'sourcePath' must be relative to the user config directory"
@@ -85,7 +93,10 @@ describe('Attachments', () => {
await fse.outputFile(someOtherPath, 'hello world');
filesToRemove.push(someOtherPath);
const copier = Attachments.copyIntoAttachmentsDirectory(attachmentsPath);
const copier = Attachments.copyIntoAttachmentsDirectory({
sourceDir: USER_DATA,
targetDir: attachmentsPath,
});
const { path: relativePath, size } = await copier(someOtherPath);
const absolutePath = path.join(attachmentsPath, relativePath);

View File

@@ -1087,7 +1087,9 @@ export async function copyStickerToAttachments(
const absolutePath =
window.Signal.Migrations.getAbsoluteStickerPath(stickerPath);
const { path, size } =
await window.Signal.Migrations.copyIntoAttachmentsDirectory(absolutePath);
await window.Signal.Migrations.copyStickerIntoAttachmentsDirectory(
absolutePath
);
const newSticker: AttachmentType = {
...sticker,

View File

@@ -59,14 +59,19 @@ export const createPlaintextReader = (
};
};
export const copyIntoAttachmentsDirectory = (
root: string
): ((sourcePath: string) => Promise<{ path: string; size: number }>) => {
if (!isString(root)) {
throw new TypeError("'root' must be a path");
export const copyIntoAttachmentsDirectory = ({
sourceDir,
targetDir,
}: {
sourceDir: string;
targetDir: string;
}): ((sourcePath: string) => Promise<{ path: string; size: number }>) => {
if (!isString(sourceDir)) {
throw new TypeError("'sourceDir' must be a path");
}
if (!isString(targetDir)) {
throw new TypeError("'targetDir' must be a path");
}
const userDataPath = window.SignalContext.getPath('userData');
return async (
sourcePath: string
@@ -75,7 +80,7 @@ export const copyIntoAttachmentsDirectory = (
throw new TypeError('sourcePath must be a string');
}
if (!isPathInside(sourcePath, userDataPath)) {
if (!isPathInside(sourcePath, sourceDir)) {
throw new Error(
"'sourcePath' must be relative to the user config directory"
);
@@ -83,9 +88,9 @@ export const copyIntoAttachmentsDirectory = (
const name = createName();
const relativePath = getRelativePath(name);
const absolutePath = join(root, relativePath);
const absolutePath = join(targetDir, relativePath);
const normalized = normalize(absolutePath);
if (!isPathInside(normalized, root)) {
if (!isPathInside(normalized, targetDir)) {
throw new Error('Invalid relative path');
}