Files
ThreadNet-Web/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

188 lines
6.7 KiB
TypeScript
Raw Normal View History

2017-01-24 15:57:42 +00:00
/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
2024-09-09 14:57:16 +01:00
Copyright 2017 Vector Creations Ltd
2017-01-24 15:57:42 +00:00
2024-09-09 14:57:16 +01:00
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
2017-01-24 15:57:42 +00:00
*/
2021-06-29 13:11:58 +01:00
import React, { createRef } from "react";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
2021-10-27 21:53:09 -05:00
import { logger } from "matrix-js-sdk/src/logger";
import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption";
import { _t } from "../../../../languageHandler";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import Field from "../../../../components/views/elements/Field";
2021-10-15 16:30:53 +02:00
function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
2017-01-24 15:57:42 +00:00
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
if (e.target?.result) {
resolve(e.target.result as ArrayBuffer);
} else {
reject(new Error("Failed to read file due to unknown error"));
}
2017-01-24 15:57:42 +00:00
};
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
}
enum Phase {
Edit = "edit",
Importing = "importing",
}
2017-01-24 15:57:42 +00:00
interface IProps {
matrixClient: MatrixClient;
onFinished(imported?: boolean): void;
}
2017-01-24 15:57:42 +00:00
interface IState {
enableSubmit: boolean;
phase: Phase;
errStr: string | null;
passphrase: string;
}
export default class ImportE2eKeysDialog extends React.Component<IProps, IState> {
private unmounted = false;
private file = createRef<HTMLInputElement>();
public constructor(props: IProps) {
2020-08-29 12:14:16 +01:00
super(props);
2017-01-24 15:57:42 +00:00
2020-08-29 12:14:16 +01:00
this.state = {
enableSubmit: false,
phase: Phase.Edit,
2020-08-29 12:14:16 +01:00
errStr: null,
passphrase: "",
2020-08-29 12:14:16 +01:00
};
}
public componentWillUnmount(): void {
this.unmounted = true;
2020-08-29 12:14:16 +01:00
}
2017-01-24 15:57:42 +00:00
private onFormChange = (): void => {
const files = this.file.current?.files;
2017-01-24 15:57:42 +00:00
this.setState({
2023-02-13 11:39:16 +00:00
enableSubmit: this.state.passphrase !== "" && !!files?.length,
2017-01-24 15:57:42 +00:00
});
2020-08-29 12:14:16 +01:00
};
2017-01-24 15:57:42 +00:00
private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({ passphrase: ev.target.value }, this.onFormChange); // update general form state too
};
private onFormSubmit = (ev: React.FormEvent): boolean => {
2017-01-24 15:57:42 +00:00
ev.preventDefault();
// noinspection JSIgnoredPromiseFromCall
const file = this.file.current?.files?.[0];
if (file) {
this.startImport(file, this.state.passphrase);
}
2017-01-24 15:57:42 +00:00
return false;
2020-08-29 12:14:16 +01:00
};
2017-01-24 15:57:42 +00:00
private startImport(file: File, passphrase: string): Promise<void> {
2017-01-24 15:57:42 +00:00
this.setState({
errStr: null,
phase: Phase.Importing,
2017-01-24 15:57:42 +00:00
});
return readFileAsArrayBuffer(file)
.then((arrayBuffer) => {
return MegolmExportEncryption.decryptMegolmKeyFile(arrayBuffer, passphrase);
})
.then((keys) => {
return this.props.matrixClient.getCrypto()!.importRoomKeysAsJson(keys);
2017-01-24 15:57:42 +00:00
})
.then(() => {
// TODO: it would probably be nice to give some feedback about what we've imported here.
this.props.onFinished(true);
})
.catch((e) => {
2021-10-15 16:30:53 +02:00
logger.error("Error importing e2e keys:", e);
if (this.unmounted) {
2017-01-24 15:57:42 +00:00
return;
}
const msg = e.friendlyText || _t("error|unknown");
2017-01-24 15:57:42 +00:00
this.setState({
errStr: msg,
phase: Phase.Edit,
2022-12-12 12:24:14 +01:00
});
2017-01-24 15:57:42 +00:00
});
2020-08-29 12:14:16 +01:00
}
2017-01-24 15:57:42 +00:00
private onCancelClick = (ev: React.MouseEvent): boolean => {
ev.preventDefault();
this.props.onFinished(false);
return false;
2020-08-29 12:14:16 +01:00
};
public render(): React.ReactNode {
const disableForm = this.state.phase !== Phase.Edit;
2017-01-24 15:57:42 +00:00
return (
<BaseDialog
className="mx_importE2eKeysDialog"
onFinished={this.props.onFinished}
2023-09-28 12:51:30 +05:30
title={_t("settings|key_export_import|import_title")}
2017-01-24 15:57:42 +00:00
>
<form onSubmit={this.onFormSubmit}>
2017-01-24 15:57:42 +00:00
<div className="mx_Dialog_content">
2023-09-28 12:51:30 +05:30
<p>{_t("settings|key_export_import|import_description_1")}</p>
<p>{_t("settings|key_export_import|import_description_2")}</p>
2017-01-24 15:57:42 +00:00
<div className="error">{this.state.errStr}</div>
<div className="mx_E2eKeysDialog_inputTable">
<div className="mx_E2eKeysDialog_inputRow">
2021-04-27 17:23:27 +02:00
<div className="mx_E2eKeysDialog_inputLabel">
2023-09-28 12:51:30 +05:30
<label htmlFor="importFile">
{_t("settings|key_export_import|file_to_import")}
</label>
2021-04-27 17:23:27 +02:00
</div>
<div className="mx_E2eKeysDialog_inputCell">
<input
ref={this.file}
2021-04-27 17:23:27 +02:00
id="importFile"
type="file"
autoFocus={true}
onChange={this.onFormChange}
2021-04-27 17:23:27 +02:00
disabled={disableForm}
/>
</div>
2017-01-24 15:57:42 +00:00
</div>
<div className="mx_E2eKeysDialog_inputRow">
<Field
2023-09-28 12:51:30 +05:30
label={_t("settings|key_export_import|enter_passphrase")}
value={this.state.passphrase}
onChange={this.onPassphraseChange}
size={64}
type="password"
disabled={disableForm}
/>
2017-01-24 15:57:42 +00:00
</div>
</div>
</div>
<div className="mx_Dialog_buttons">
2021-07-23 10:23:45 +01:00
<input
className="mx_Dialog_primary"
type="submit"
value={_t("action|import")}
2017-01-24 15:57:42 +00:00
disabled={!this.state.enableSubmit || disableForm}
/>
<button onClick={this.onCancelClick} disabled={disableForm}>
{_t("action|cancel")}
</button>
2017-01-24 15:57:42 +00:00
</div>
</form>
</BaseDialog>
);
2020-08-29 12:14:16 +01:00
}
}