Don't access RemoteConfig directly from 'dumb' components
This commit is contained in:
parent
e79380b37c
commit
0134990275
40 changed files with 352 additions and 353 deletions
|
@ -1,71 +1,99 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { isString } from 'lodash';
|
||||
import { join, normalize } from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import { omit } from 'lodash';
|
||||
import { blobToArrayBuffer } from 'blob-util';
|
||||
import * as log from '../logging/log';
|
||||
import { getValue } from '../RemoteConfig';
|
||||
|
||||
import { isPathInside } from './isPathInside';
|
||||
import { parseIntOrThrow } from './parseIntOrThrow';
|
||||
import { scaleImageToLevel } from './scaleImageToLevel';
|
||||
import { isRecord } from './isRecord';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import { canBeTranscoded } from '../types/Attachment';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import * as MIME from '../types/MIME';
|
||||
|
||||
const PATH = 'attachments.noindex';
|
||||
const AVATAR_PATH = 'avatars.noindex';
|
||||
const BADGES_PATH = 'badges.noindex';
|
||||
const STICKER_PATH = 'stickers.noindex';
|
||||
const TEMP_PATH = 'temp';
|
||||
const UPDATE_CACHE_PATH = 'update-cache';
|
||||
const DRAFT_PATH = 'drafts.noindex';
|
||||
const MEBIBYTE = 1024 * 1024;
|
||||
const DEFAULT_MAX = 100 * MEBIBYTE;
|
||||
|
||||
const CACHED_PATHS = new Map<string, string>();
|
||||
export const getMaximumAttachmentSize = (): number => {
|
||||
try {
|
||||
return parseIntOrThrow(
|
||||
getValue('global.attachments.maxBytes'),
|
||||
'preProcessAttachment/maxAttachmentSize'
|
||||
);
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
'Failed to parse integer out of global.attachments.maxBytes feature flag'
|
||||
);
|
||||
return DEFAULT_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
const createPathGetter =
|
||||
(subpath: string) =>
|
||||
(userDataPath: string): string => {
|
||||
if (!isString(userDataPath)) {
|
||||
throw new TypeError("'userDataPath' must be a string");
|
||||
}
|
||||
|
||||
const naivePath = join(userDataPath, subpath);
|
||||
|
||||
const cached = CACHED_PATHS.get(naivePath);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
let result = naivePath;
|
||||
if (fse.pathExistsSync(naivePath)) {
|
||||
result = fse.realpathSync(naivePath);
|
||||
}
|
||||
|
||||
CACHED_PATHS.set(naivePath, result);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getAvatarsPath = createPathGetter(AVATAR_PATH);
|
||||
export const getBadgesPath = createPathGetter(BADGES_PATH);
|
||||
export const getDraftPath = createPathGetter(DRAFT_PATH);
|
||||
export const getPath = createPathGetter(PATH);
|
||||
export const getStickersPath = createPathGetter(STICKER_PATH);
|
||||
export const getTempPath = createPathGetter(TEMP_PATH);
|
||||
export const getUpdateCachePath = createPathGetter(UPDATE_CACHE_PATH);
|
||||
|
||||
export const createDeleter = (
|
||||
root: string
|
||||
): ((relativePath: string) => Promise<void>) => {
|
||||
if (!isString(root)) {
|
||||
throw new TypeError("'root' must be a path");
|
||||
// Upgrade steps
|
||||
// NOTE: This step strips all EXIF metadata from JPEG images as
|
||||
// part of re-encoding the image:
|
||||
export async function autoOrientJPEG(
|
||||
attachment: AttachmentType,
|
||||
{ logger }: { logger: LoggerType },
|
||||
{
|
||||
sendHQImages = false,
|
||||
isIncoming = false,
|
||||
}: {
|
||||
sendHQImages?: boolean;
|
||||
isIncoming?: boolean;
|
||||
} = {}
|
||||
): Promise<AttachmentType> {
|
||||
if (isIncoming && !MIME.isJPEG(attachment.contentType)) {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
return async (relativePath: string): Promise<void> => {
|
||||
if (!isString(relativePath)) {
|
||||
throw new TypeError("'relativePath' must be a string");
|
||||
}
|
||||
if (!canBeTranscoded(attachment)) {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
const absolutePath = join(root, relativePath);
|
||||
const normalized = normalize(absolutePath);
|
||||
if (!isPathInside(normalized, root)) {
|
||||
throw new Error('Invalid relative path');
|
||||
}
|
||||
await fse.remove(absolutePath);
|
||||
};
|
||||
};
|
||||
// If we haven't downloaded the attachment yet, we won't have the data.
|
||||
// All images go through handleImageAttachment before being sent and thus have
|
||||
// already been scaled to level, oriented, stripped of exif data, and saved
|
||||
// in high quality format. If we want to send the image in HQ we can return
|
||||
// the attachment as-is. Otherwise we'll have to further scale it down.
|
||||
if (!attachment.data || sendHQImages) {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
const dataBlob = new Blob([attachment.data], {
|
||||
type: attachment.contentType,
|
||||
});
|
||||
try {
|
||||
const { blob: xcodedDataBlob } = await scaleImageToLevel(
|
||||
dataBlob,
|
||||
attachment.contentType,
|
||||
isIncoming
|
||||
);
|
||||
const xcodedDataArrayBuffer = await blobToArrayBuffer(xcodedDataBlob);
|
||||
|
||||
// IMPORTANT: We overwrite the existing `data` `Uint8Array` losing the original
|
||||
// image data. Ideally, we’d preserve the original image data for users who want to
|
||||
// retain it but due to reports of data loss, we don’t want to overburden IndexedDB
|
||||
// by potentially doubling stored image data.
|
||||
// See: https://github.com/signalapp/Signal-Desktop/issues/1589
|
||||
const xcodedAttachment = {
|
||||
// `digest` is no longer valid for auto-oriented image data, so we discard it:
|
||||
...omit(attachment, 'digest'),
|
||||
data: new Uint8Array(xcodedDataArrayBuffer),
|
||||
size: xcodedDataArrayBuffer.byteLength,
|
||||
};
|
||||
|
||||
return xcodedAttachment;
|
||||
} catch (error: unknown) {
|
||||
const errorString =
|
||||
isRecord(error) && 'stack' in error ? error.stack : error;
|
||||
logger.error(
|
||||
'autoOrientJPEG: Failed to rotate/scale attachment',
|
||||
errorString
|
||||
);
|
||||
|
||||
return attachment;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue