Files
Desktop/ts/util/messageFilePaths.std.ts
2026-02-09 09:37:27 -08:00

123 lines
3.5 KiB
TypeScript

// Copyright 2018 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { MessageAttributesType } from '../model-types.d.ts';
import type { AttachmentType } from '../types/Attachment.std.js';
export function getFilePathsReferencedByAttachment(
attachment: AttachmentType
): {
externalAttachments: Set<string>;
externalDownloads: Set<string>;
} {
const externalAttachments = new Set<string>();
const externalDownloads = new Set<string>();
// Copied attachments weakly reference their paths -- there is a chance that
// non-normalized attachments may still exist on messages.json, in which case
// `isAttachmentSafeToDelete` would incorrectly mark a copied thumbnail as safe to
// delete
if (attachment.copied) {
return { externalAttachments, externalDownloads };
}
const { path, thumbnail, screenshot, thumbnailFromBackup, downloadPath } =
attachment;
if (path) {
externalAttachments.add(path);
}
// downloadPath is relative to downloads folder and has to be tracked
// separately.
if (downloadPath) {
externalDownloads.add(downloadPath);
}
if (thumbnail && thumbnail.path) {
externalAttachments.add(thumbnail.path);
}
if (screenshot && screenshot.path) {
externalAttachments.add(screenshot.path);
}
if (thumbnailFromBackup && thumbnailFromBackup.path) {
externalAttachments.add(thumbnailFromBackup.path);
}
return { externalAttachments, externalDownloads };
}
function getFilePathsForVersionOfMessage(
rootOrEditHistoryMessage: Partial<MessageAttributesType>
): {
externalAttachments: Set<string>;
externalDownloads: Set<string>;
} {
const externalAttachments = new Set<string>();
const externalDownloads = new Set<string>();
function addFilePathsReferencedByAttachment(attachment: AttachmentType) {
const result = getFilePathsReferencedByAttachment(attachment);
result.externalAttachments.forEach(path => externalAttachments.add(path));
result.externalDownloads.forEach(path => externalDownloads.add(path));
}
const { attachments, bodyAttachment, contact, quote, preview, sticker } =
rootOrEditHistoryMessage;
attachments?.forEach(addFilePathsReferencedByAttachment);
if (bodyAttachment) {
addFilePathsReferencedByAttachment(bodyAttachment);
}
if (quote?.attachments) {
quote.attachments.forEach(attachment => {
if (attachment.thumbnail) {
addFilePathsReferencedByAttachment(attachment.thumbnail);
}
});
}
if (contact) {
contact.forEach(item => {
if (item.avatar?.avatar) {
addFilePathsReferencedByAttachment(item.avatar.avatar);
}
});
}
if (preview) {
preview.forEach(item => {
if (item.image) {
addFilePathsReferencedByAttachment(item.image);
}
});
}
if (sticker?.data) {
addFilePathsReferencedByAttachment(sticker.data);
}
return { externalAttachments, externalDownloads };
}
export function getFilePathsReferencedByMessage(
message: MessageAttributesType
): {
externalAttachments: Array<string>;
externalDownloads: Array<string>;
} {
const externalAttachments = new Set<string>();
const externalDownloads = new Set<string>();
[message, ...(message.editHistory ?? [])].forEach(version => {
const result = getFilePathsForVersionOfMessage(version);
result.externalAttachments.forEach(path => externalAttachments.add(path));
result.externalDownloads.forEach(path => externalDownloads.add(path));
});
return {
externalAttachments: [...externalAttachments],
externalDownloads: [...externalDownloads],
};
}