diff --git a/src/async-components/views/dialogs/ImportE2eKeysDialog.js b/src/async-components/views/dialogs/ImportE2eKeysDialog.js new file mode 100644 index 0000000000..586bd9b6cc --- /dev/null +++ b/src/async-components/views/dialogs/ImportE2eKeysDialog.js @@ -0,0 +1,170 @@ +/* +Copyright 2017 Vector Creations Ltd + +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. +*/ + +import React from 'react'; + +import * as Matrix from 'matrix-js-sdk'; +import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption'; +import sdk from '../../../index'; + +function readFileAsArrayBuffer(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (e) => { + resolve(e.target.result); + }; + reader.onerror = reject; + + reader.readAsArrayBuffer(file); + }); +} + +const PHASE_EDIT = 1; +const PHASE_IMPORTING = 2; + +export default React.createClass({ + displayName: 'ImportE2eKeysDialog', + + propTypes: { + matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired, + onFinished: React.PropTypes.func.isRequired, + }, + + getInitialState: function() { + return { + enableSubmit: false, + phase: PHASE_EDIT, + errStr: null, + }; + }, + + componentWillMount: function() { + this._unmounted = false; + }, + + componentWillUnmount: function() { + this._unmounted = true; + }, + + _onFormChange: function(ev) { + const files = this.refs.file.files || []; + this.setState({ + enableSubmit: (this.refs.passphrase.value !== "" && files.length > 0), + }); + }, + + _onFormSubmit: function(ev) { + ev.preventDefault(); + this._startImport(this.refs.file.files[0], this.refs.passphrase.value); + return false; + }, + + _startImport: function(file, passphrase) { + this.setState({ + errStr: null, + phase: PHASE_IMPORTING, + }); + + return readFileAsArrayBuffer(file).then((arrayBuffer) => { + return MegolmExportEncryption.decryptMegolmKeyFile( + arrayBuffer, passphrase + ); + }).then((keys) => { + return this.props.matrixClient.importRoomKeys(JSON.parse(keys)); + }).then(() => { + // TODO: it would probably be nice to give some feedback about what we've imported here. + this.props.onFinished(true); + }).catch((e) => { + if (this._unmounted) { + return; + } + this.setState({ + errStr: e.message, + phase: PHASE_EDIT, + }); + }); + }, + + render: function() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); + + const disableForm = (this.state.phase !== PHASE_EDIT); + + return ( + +
+
+

+ This process allows you to import encryption keys + that you had previously exported from another Matrix + client. You will then be able to decrypt any + messages that the other client could decrypt. +

+

+ The export file will be protected with a passphrase. + You should enter the passphrase here, to decrypt the + file. +

+
+ {this.state.errStr} +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + Cancel + +
+
+
+ ); + }, +}); diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 6edf572f6c..d64f0383f6 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -396,7 +396,21 @@ module.exports = React.createClass({ _onExportE2eKeysClicked: function() { Modal.createDialogAsync( (cb) => { - require(['../../async-components/views/dialogs/ExportE2eKeysDialog'], cb); + require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => { + cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog')); + }, "e2e-export"); + }, { + matrixClient: MatrixClientPeg.get(), + } + ); + }, + + _onImportE2eKeysClicked: function() { + Modal.createDialogAsync( + (cb) => { + require.ensure(['../../async-components/views/dialogs/ImportE2eKeysDialog'], () => { + cb(require('../../async-components/views/dialogs/ImportE2eKeysDialog')); + }, "e2e-export"); }, { matrixClient: MatrixClientPeg.get(), } @@ -473,7 +487,8 @@ module.exports = React.createClass({ const deviceId = client.deviceId; const identityKey = client.getDeviceEd25519Key() || ""; - let exportButton = null; + let exportButton = null, + importButton = null; if (client.isCryptoEnabled) { exportButton = ( @@ -482,6 +497,12 @@ module.exports = React.createClass({ Export E2E room keys ); + importButton = ( + + Import E2E room keys + + ); } return (
@@ -492,6 +513,7 @@ module.exports = React.createClass({
  • {identityKey}
  • {exportButton} + {importButton}
    ); diff --git a/src/index.js b/src/index.js index 0e3e90aed6..b6d8c0b5f4 100644 --- a/src/index.js +++ b/src/index.js @@ -27,16 +27,3 @@ module.exports.resetSkin = function() { module.exports.getComponent = function(componentName) { return Skinner.getComponent(componentName); }; - - -/* -window.importKeys = function(password, data) { - const arrayBuffer = new TextEncoder().encode(data).buffer; - return MegolmExportEncryption.decryptMegolmKeyFile( - arrayBuffer, password - ).then((j) => { - const k = JSON.parse(j); - return MatrixClientPeg.get().importRoomKeys(k); - }); -}; -*/