diff --git a/src/@types/browser-encrypt-attachment.ts b/src/@types/browser-encrypt-attachment.ts new file mode 100644 index 00000000000..a8249ab3505 --- /dev/null +++ b/src/@types/browser-encrypt-attachment.ts @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +declare module "browser-encrypt-attachment" { + interface IInfo { + v: string; + key: { + alg: string; + key_ops: string[]; // eslint-disable-line camelcase + kty: string; + k: string; + ext: boolean; + }; + iv: string; + hashes: {[alg: string]: string}; + } + + interface IEncryptedAttachment { + data: ArrayBuffer; + info: IInfo; + } + + export function encryptAttachment(plaintextBuffer: ArrayBuffer): Promise; + export function decryptAttachment(ciphertextBuffer: ArrayBuffer, info: IInfo): Promise; +} diff --git a/src/@types/png-chunks-extract.ts b/src/@types/png-chunks-extract.ts new file mode 100644 index 00000000000..697e68d343b --- /dev/null +++ b/src/@types/png-chunks-extract.ts @@ -0,0 +1,26 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +declare module "png-chunks-extract" { + interface IChunk { + name: string; + data: Uint8Array; + } + + function extractPngChunks(data: Uint8Array | Buffer): IChunk[]; + + export default extractPngChunks; +} diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index 9831a9b41db..b85c0462b5d 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -18,6 +18,9 @@ limitations under the License. import React from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; +import { IEncryptedFile, IMediaEventInfo } from "./customisations/models/IMediaEventContent"; +import { IUploadOpts } from "matrix-js-sdk/src/@types/requests"; +import { MsgType } from "matrix-js-sdk/src/@types/event"; import dis from './dispatcher/dispatcher'; import * as sdk from './index'; @@ -307,7 +310,7 @@ function loadVideoElement(videoFile): Promise { function infoForVideoFile(matrixClient, roomId, videoFile) { const thumbnailType = "image/jpeg"; - let videoInfo; + let videoInfo: Partial; return loadVideoElement(videoFile).then((video) => { return createThumbnail(video, video.videoWidth, video.videoHeight, thumbnailType); }).then((result) => { @@ -356,49 +359,48 @@ export function uploadFile( matrixClient: MatrixClient, roomId: string, file: File | Blob, - progressHandler?: any, // TODO: Types -): IAbortablePromise<{url?: string, file?: any}> { // TODO: Types + progressHandler?: IUploadOpts["progressHandler"], +): IAbortablePromise<{ url?: string, file?: IEncryptedFile }> { let canceled = false; if (matrixClient.isRoomEncrypted(roomId)) { // If the room is encrypted then encrypt the file before uploading it. // First read the file into memory. - let uploadPromise; - let encryptInfo; + let uploadPromise: IAbortablePromise; const prom = readFileAsArrayBuffer(file).then(function(data) { if (canceled) throw new UploadCanceledError(); // Then encrypt the file. return encrypt.encryptAttachment(data); }).then(function(encryptResult) { if (canceled) throw new UploadCanceledError(); - // Record the information needed to decrypt the attachment. - encryptInfo = encryptResult.info; + // Pass the encrypted data as a Blob to the uploader. const blob = new Blob([encryptResult.data]); uploadPromise = matrixClient.uploadContent(blob, { - progressHandler: progressHandler, + progressHandler, includeFilename: false, }); - return uploadPromise; - }).then(function(url) { - if (canceled) throw new UploadCanceledError(); - // If the attachment is encrypted then bundle the URL along - // with the information needed to decrypt the attachment and - // add it under a file key. - encryptInfo.url = url; - if (file.type) { - encryptInfo.mimetype = file.type; - } - return { "file": encryptInfo }; - }) as IAbortablePromise<{ file: any }>; + + return uploadPromise.then(url => { + if (canceled) throw new UploadCanceledError(); + + // If the attachment is encrypted then bundle the URL along + // with the information needed to decrypt the attachment and + // add it under a file key. + return { + file: { + ...encryptResult.info, + url, + }, + }; + }); + }) as IAbortablePromise<{ file: IEncryptedFile }>; prom.abort = () => { canceled = true; if (uploadPromise) matrixClient.cancelUpload(uploadPromise); }; return prom; } else { - const basePromise = matrixClient.uploadContent(file, { - progressHandler: progressHandler, - }); + const basePromise = matrixClient.uploadContent(file, { progressHandler }); const promise1 = basePromise.then(function(url) { if (canceled) throw new UploadCanceledError(); // If the attachment isn't encrypted then include the URL directly. @@ -554,29 +556,29 @@ export default class ContentMessages { const prom = new Promise((resolve) => { if (file.type.indexOf('image/') === 0) { - content.msgtype = 'm.image'; + content.msgtype = MsgType.Image; infoForImageFile(matrixClient, roomId, file).then((imageInfo) => { Object.assign(content.info, imageInfo); resolve(); }, (e) => { logger.error(e); - content.msgtype = 'm.file'; + content.msgtype = MsgType.File; resolve(); }); } else if (file.type.indexOf('audio/') === 0) { - content.msgtype = 'm.audio'; + content.msgtype = MsgType.Audio; resolve(); } else if (file.type.indexOf('video/') === 0) { - content.msgtype = 'm.video'; + content.msgtype = MsgType.Video; infoForVideoFile(matrixClient, roomId, file).then((videoInfo) => { Object.assign(content.info, videoInfo); resolve(); }, (e) => { - content.msgtype = 'm.file'; + content.msgtype = MsgType.File; resolve(); }); } else { - content.msgtype = 'm.file'; + content.msgtype = MsgType.File; resolve(); } }) as IAbortablePromise; diff --git a/src/customisations/models/IMediaEventContent.ts b/src/customisations/models/IMediaEventContent.ts index a3e0231ebca..899d03f02d5 100644 --- a/src/customisations/models/IMediaEventContent.ts +++ b/src/customisations/models/IMediaEventContent.ts @@ -18,7 +18,6 @@ export interface IEncryptedFile { url: string; - mimetype?: string; key: { alg: string; key_ops: string[]; // eslint-disable-line camelcase