2021-10-27 17:54:16 +00:00
|
|
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
import { omit } from 'lodash';
|
|
|
|
|
import { blobToArrayBuffer } from 'blob-util';
|
|
|
|
|
import * as log from '../logging/log';
|
|
|
|
|
import { getValue } from '../RemoteConfig';
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
import { parseIntOrThrow } from './parseIntOrThrow';
|
|
|
|
|
import { scaleImageToLevel } from './scaleImageToLevel';
|
|
|
|
|
import type { AttachmentType } from '../types/Attachment';
|
|
|
|
|
import { canBeTranscoded } from '../types/Attachment';
|
|
|
|
|
import type { LoggerType } from '../types/Logging';
|
|
|
|
|
import * as MIME from '../types/MIME';
|
2022-11-22 18:43:43 +00:00
|
|
|
|
import * as Errors from '../types/errors';
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2023-01-05 21:47:11 +00:00
|
|
|
|
export const KIBIBYTE = 1024;
|
2022-10-24 20:46:36 +00:00
|
|
|
|
const MEBIBYTE = 1024 * 1024;
|
|
|
|
|
const DEFAULT_MAX = 100 * MEBIBYTE;
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2023-01-05 21:47:11 +00:00
|
|
|
|
export const getMaximumAttachmentSizeInKb = (): number => {
|
2022-10-24 20:46:36 +00:00
|
|
|
|
try {
|
2023-01-05 21:47:11 +00:00
|
|
|
|
return (
|
|
|
|
|
parseIntOrThrow(
|
|
|
|
|
getValue('global.attachments.maxBytes'),
|
|
|
|
|
'preProcessAttachment/maxAttachmentSize'
|
|
|
|
|
) / KIBIBYTE
|
2022-10-24 20:46:36 +00:00
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log.warn(
|
|
|
|
|
'Failed to parse integer out of global.attachments.maxBytes feature flag'
|
|
|
|
|
);
|
2023-01-05 21:47:11 +00:00
|
|
|
|
return DEFAULT_MAX / KIBIBYTE;
|
2022-10-24 20:46:36 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
2022-04-27 00:31:01 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
2022-04-27 18:40:58 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
if (!canBeTranscoded(attachment)) {
|
|
|
|
|
return attachment;
|
|
|
|
|
}
|
2022-04-27 00:31:01 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
2022-04-27 18:40:58 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
const dataBlob = new Blob([attachment.data], {
|
|
|
|
|
type: attachment.contentType,
|
|
|
|
|
});
|
|
|
|
|
try {
|
|
|
|
|
const { blob: xcodedDataBlob } = await scaleImageToLevel(
|
|
|
|
|
dataBlob,
|
|
|
|
|
attachment.contentType,
|
|
|
|
|
isIncoming
|
|
|
|
|
);
|
|
|
|
|
const xcodedDataArrayBuffer = await blobToArrayBuffer(xcodedDataBlob);
|
2022-04-27 18:40:58 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
// 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,
|
|
|
|
|
};
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
return xcodedAttachment;
|
|
|
|
|
} catch (error: unknown) {
|
2022-11-22 18:43:43 +00:00
|
|
|
|
const errorString = Errors.toLogFormat(error);
|
2022-10-24 20:46:36 +00:00
|
|
|
|
logger.error(
|
|
|
|
|
'autoOrientJPEG: Failed to rotate/scale attachment',
|
|
|
|
|
errorString
|
|
|
|
|
);
|
2021-10-27 17:54:16 +00:00
|
|
|
|
|
2022-10-24 20:46:36 +00:00
|
|
|
|
return attachment;
|
2021-10-27 17:54:16 +00:00
|
|
|
|
}
|
2022-10-24 20:46:36 +00:00
|
|
|
|
}
|