Differentiate download and decryption errors when showing images (#9562)
This commit is contained in:
committed by
GitHub
parent
abec724387
commit
962e8e0b23
@@ -39,6 +39,7 @@ import { blobIsAnimated, mayBeAnimated } from '../../../utils/Image';
|
||||
import { presentableTextForFile } from "../../../utils/FileUtils";
|
||||
import { createReconnectedListener } from '../../../utils/connection';
|
||||
import MediaProcessingError from './shared/MediaProcessingError';
|
||||
import { DecryptError, DownloadError } from "../../../utils/DecryptFile";
|
||||
|
||||
enum Placeholder {
|
||||
NoImage,
|
||||
@@ -258,7 +259,15 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||
]));
|
||||
} catch (error) {
|
||||
if (this.unmounted) return;
|
||||
logger.warn("Unable to decrypt attachment: ", error);
|
||||
|
||||
if (error instanceof DecryptError) {
|
||||
logger.error("Unable to decrypt attachment: ", error);
|
||||
} else if (error instanceof DownloadError) {
|
||||
logger.error("Unable to download attachment to decrypt it: ", error);
|
||||
} else {
|
||||
logger.error("Error encountered when downloading encrypted attachment: ", error);
|
||||
}
|
||||
|
||||
// Set a placeholder image when we can't decrypt the image.
|
||||
this.setState({ error });
|
||||
}
|
||||
@@ -557,9 +566,16 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||
const content = this.props.mxEvent.getContent<IMediaEventContent>();
|
||||
|
||||
if (this.state.error) {
|
||||
let errorText = _t("Unable to show image due to error");
|
||||
if (this.state.error instanceof DecryptError) {
|
||||
errorText = _t("Error decrypting image");
|
||||
} else if (this.state.error instanceof DownloadError) {
|
||||
errorText = _t("Error downloading image");
|
||||
}
|
||||
|
||||
return (
|
||||
<MediaProcessingError className="mx_MImageBody">
|
||||
{ _t("Error decrypting image") }
|
||||
{ errorText }
|
||||
</MediaProcessingError>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2312,7 +2312,9 @@
|
||||
"Decrypt %(text)s": "Decrypt %(text)s",
|
||||
"Invalid file%(extra)s": "Invalid file%(extra)s",
|
||||
"Image": "Image",
|
||||
"Unable to show image due to error": "Unable to show image due to error",
|
||||
"Error decrypting image": "Error decrypting image",
|
||||
"Error downloading image": "Error downloading image",
|
||||
"Show image": "Show image",
|
||||
"Join the conference at the top of this room": "Join the conference at the top of this room",
|
||||
"Join the conference from the room information card on the right": "Join the conference from the room information card on the right",
|
||||
|
||||
@@ -16,11 +16,28 @@ limitations under the License.
|
||||
|
||||
// Pull in the encryption lib so that we can decrypt attachments.
|
||||
import encrypt from 'matrix-encrypt-attachment';
|
||||
import { parseErrorResponse } from 'matrix-js-sdk/src/http-api';
|
||||
|
||||
import { mediaFromContent } from "../customisations/Media";
|
||||
import { IEncryptedFile, IMediaEventInfo } from "../customisations/models/IMediaEventContent";
|
||||
import { getBlobSafeMimeType } from "./blobs";
|
||||
|
||||
export class DownloadError extends Error {
|
||||
constructor(e) {
|
||||
super(e.message);
|
||||
this.name = "DownloadError";
|
||||
this.stack = e.stack;
|
||||
}
|
||||
}
|
||||
|
||||
export class DecryptError extends Error {
|
||||
constructor(e) {
|
||||
super(e.message);
|
||||
this.name = "DecryptError";
|
||||
this.stack = e.stack;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a file attached to a matrix event.
|
||||
* @param {IEncryptedFile} file The encrypted file information taken from the matrix event.
|
||||
@@ -30,19 +47,27 @@ import { getBlobSafeMimeType } from "./blobs";
|
||||
* @param {IMediaEventInfo} info The info parameter taken from the matrix event.
|
||||
* @returns {Promise<Blob>} Resolves to a Blob of the file.
|
||||
*/
|
||||
export function decryptFile(
|
||||
export async function decryptFile(
|
||||
file: IEncryptedFile,
|
||||
info?: IMediaEventInfo,
|
||||
): Promise<Blob> {
|
||||
const media = mediaFromContent({ file });
|
||||
// Download the encrypted file as an array buffer.
|
||||
return media.downloadSource().then((response) => {
|
||||
return response.arrayBuffer();
|
||||
}).then((responseData) => {
|
||||
// Decrypt the array buffer using the information taken from
|
||||
// the event content.
|
||||
return encrypt.decryptAttachment(responseData, file);
|
||||
}).then((dataArray) => {
|
||||
|
||||
let responseData: ArrayBuffer;
|
||||
try {
|
||||
// Download the encrypted file as an array buffer.
|
||||
const response = await media.downloadSource();
|
||||
if (!response.ok) {
|
||||
throw parseErrorResponse(response, await response.text());
|
||||
}
|
||||
responseData = await response.arrayBuffer();
|
||||
} catch (e) {
|
||||
throw new DownloadError(e);
|
||||
}
|
||||
|
||||
try {
|
||||
// Decrypt the array buffer using the information taken from the event content.
|
||||
const dataArray = await encrypt.decryptAttachment(responseData, file);
|
||||
// Turn the array into a Blob and give it the correct MIME-type.
|
||||
|
||||
// IMPORTANT: we must not allow scriptable mime-types into Blobs otherwise
|
||||
@@ -53,5 +78,7 @@ export function decryptFile(
|
||||
mimetype = getBlobSafeMimeType(mimetype);
|
||||
|
||||
return new Blob([dataArray], { type: mimetype });
|
||||
});
|
||||
} catch (e) {
|
||||
throw new DecryptError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,12 +48,10 @@ export class LazyValue<T> {
|
||||
if (this.prom) return this.prom;
|
||||
this.prom = this.getFn();
|
||||
|
||||
// Fork the promise chain to avoid accidentally making it return undefined always.
|
||||
this.prom.then(v => {
|
||||
return this.prom.then(v => {
|
||||
this.val = v;
|
||||
this.done = true;
|
||||
return v;
|
||||
});
|
||||
|
||||
return this.prom;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user