diff --git a/package.json b/package.json
index 1eaee39c41..e0cfb72148 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"react-dom": "^15.4.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.11.1",
+ "text-encoding-utf-8": "^1.0.1",
"velocity-vector": "vector-im/velocity#059e3b2",
"whatwg-fetch": "^1.0.0"
},
diff --git a/src/async-components/views/dialogs/ExportE2eKeysDialog.js b/src/async-components/views/dialogs/ExportE2eKeysDialog.js
new file mode 100644
index 0000000000..284d299f4b
--- /dev/null
+++ b/src/async-components/views/dialogs/ExportE2eKeysDialog.js
@@ -0,0 +1,84 @@
+/*
+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 sdk from '../../../index';
+
+import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
+
+export default React.createClass({
+ displayName: 'ExportE2eKeysDialog',
+
+ getInitialState: function() {
+ return {
+ collectedPassword: false,
+ };
+ },
+
+ _onPassphraseFormSubmit: function(ev) {
+ ev.preventDefault();
+ console.log(this.refs.passphrase1.value);
+ return false;
+ },
+
+ render: function() {
+ let content;
+ if (!this.state.collectedPassword) {
+ content = (
+
+
+ This process will allow you to export the keys for messages
+ you have received in encrypted rooms to a local file. You
+ will then be able to import the file into another Matrix
+ client in the future, so that client will also be able to
+ decrypt these messages.
+
+
+ The exported file will allow anyone who can read it to decrypt
+ any encrypted messages that you can see, so you should be
+ careful to keep it secure. To help with this, you should enter
+ a passphrase below, which will be used to encrypt the exported
+ data. It will only be possible to import the data by using the
+ same passphrase.
+
+
+
+ );
+ }
+
+ return (
+
+
+ Export room keys
+
+ {content}
+
+ );
+ },
+});
diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.js
index 351f58aaa6..e3ca7e68f2 100644
--- a/src/utils/MegolmExportEncryption.js
+++ b/src/utils/MegolmExportEncryption.js
@@ -17,13 +17,14 @@ limitations under the License.
"use strict";
// polyfill textencoder if necessary
+import * as TextEncodingUtf8 from 'text-encoding-utf-8';
let TextEncoder = window.TextEncoder;
if (!TextEncoder) {
- TextEncoder = require('./TextEncoderPolyfill');
+ TextEncoder = TextEncodingUtf8.TextEncoder;
}
let TextDecoder = window.TextDecoder;
-if (TextDecoder) {
- TextDecoder = require('./TextDecoderPolyfill');
+if (!TextDecoder) {
+ TextDecoder = TextEncodingUtf8.TextDecoder;
}
const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle;
diff --git a/src/utils/TextDecoderPolyfill.js b/src/utils/TextDecoderPolyfill.js
deleted file mode 100644
index e203676bb7..0000000000
--- a/src/utils/TextDecoderPolyfill.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
-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.
-*/
-
-"use strict";
-
-// Polyfill for TextDecoder.
-
-const REPLACEMENT_CHAR = '\uFFFD';
-
-export default class TextDecoder {
- /**
- * Decode a UTF-8 byte array as a javascript string
- *
- * @param {Uint8Array} u8Array UTF-8-encoded onput
- * @return {str}
- */
- decode(u8Array) {
- let u0, u1, u2, u3;
-
- let str = '';
- let idx = 0;
- while (idx < u8Array.length) {
- u0 = u8Array[idx++];
- if (!(u0 & 0x80)) {
- str += String.fromCharCode(u0);
- continue;
- }
-
- if ((u0 & 0xC0) != 0xC0) {
- // continuation byte where we expect a leading byte
- str += REPLACEMENT_CHAR;
- continue;
- }
-
- if (u0 > 0xF4) {
- // this would imply a 5-byte or longer encoding, which is
- // invalid and unsupported here.
- str += REPLACEMENT_CHAR;
- continue;
- }
-
- u1 = u8Array[idx++];
- if (u1 === undefined) {
- str += REPLACEMENT_CHAR;
- continue;
- }
-
- if ((u1 & 0xC0) != 0x80) {
- // leading byte where we expect a continuation byte
- str += REPLACEMENT_CHAR.repeat(2);
- continue;
- }
- u1 &= 0x3F;
- if (!(u0 & 0x20)) {
- const u = ((u0 & 0x1F) << 6) | u1;
- if (u < 0x80) {
- // over-long
- str += REPLACEMENT_CHAR.repeat(2);
- } else {
- str += String.fromCharCode(u);
- }
- continue;
- }
-
- u2 = u8Array[idx++];
- if (u2 === undefined) {
- str += REPLACEMENT_CHAR.repeat(2);
- continue;
- }
- if ((u2 & 0xC0) != 0x80) {
- // leading byte where we expect a continuation byte
- str += REPLACEMENT_CHAR.repeat(3);
- continue;
- }
- u2 &= 0x3F;
- if (!(u0 & 0x10)) {
- const u = ((u0 & 0x0F) << 12) | (u1 << 6) | u2;
- if (u < 0x800) {
- // over-long
- str += REPLACEMENT_CHAR.repeat(3);
- } else if (u == 0xFEFF && idx == 3) {
- // byte-order mark: do not add to output
- } else {
- str += String.fromCharCode(u);
- }
- continue;
- }
-
- u3 = u8Array[idx++];
- if (u3 === undefined) {
- str += REPLACEMENT_CHAR.repeat(3);
- continue;
- }
- if ((u3 & 0xC0) != 0x80) {
- // leading byte where we expect a continuation byte
- str += REPLACEMENT_CHAR.repeat(4);
- continue;
- }
- u3 &= 0x3F;
- const u = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | u3;
- if (u < 0x10000) {
- // over-long
- str += REPLACEMENT_CHAR.repeat(4);
- continue;
- }
- if (u > 0x1FFFF) {
- // unicode stops here.
- str += REPLACEMENT_CHAR.repeat(4);
- continue;
- }
-
- // encode as utf-16
- const v = u - 0x10000;
- str += String.fromCharCode(0xD800 | (v >> 10), 0xDC00 | (v & 0x3FF));
- }
- return str;
- }
-}
diff --git a/src/utils/TextEncoderPolyfill.js b/src/utils/TextEncoderPolyfill.js
deleted file mode 100644
index 41ee4782a9..0000000000
--- a/src/utils/TextEncoderPolyfill.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-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.
-*/
-
-"use strict";
-
-// Polyfill for TextEncoder. Based on emscripten's stringToUTF8Array.
-
-function utf8len(str) {
- var len = 0;
- for (var i = 0; i < str.length; ++i) {
- var u = str.charCodeAt(i);
- if (u >= 0xD800 && u <= 0xDFFF && i < str.length-1) {
- // lead surrogate - combine with next surrogate
- u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
- }
-
- if (u <= 0x7F) {
- ++len;
- } else if (u <= 0x7FF) {
- len += 2;
- } else if (u <= 0xFFFF) {
- len += 3;
- } else {
- len += 4;
- }
- }
- return len;
-}
-
-export default class TextEncoder {
- /**
- * Encode a javascript string as utf-8
- *
- * @param {String} str String to encode
- * @return {Uint8Array} UTF-8-encoded output
- */
- encode(str) {
- const outU8Array = new Uint8Array(utf8len(str));
- var outIdx = 0;
- for (var i = 0; i < str.length; ++i) {
- var u = str.charCodeAt(i);
- if (u >= 0xD800 && u <= 0xDFFF && i < str.length-1) {
- // lead surrogate - combine with next surrogate
- u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
- }
-
- if (u <= 0x7F) {
- outU8Array[outIdx++] = u;
- } else if (u <= 0x7FF) {
- outU8Array[outIdx++] = 0xC0 | (u >> 6);
- outU8Array[outIdx++] = 0x80 | (u & 0x3F);
- } else if (u <= 0xFFFF) {
- outU8Array[outIdx++] = 0xE0 | (u >> 12);
- outU8Array[outIdx++] = 0x80 | ((u >> 6) & 0x3F);
- outU8Array[outIdx++] = 0x80 | (u & 0x3F);
- } else {
- outU8Array[outIdx++] = 0xF0 | (u >> 18);
- outU8Array[outIdx++] = 0x80 | ((u >> 12) & 0x3F);
- outU8Array[outIdx++] = 0x80 | ((u >> 6) & 0x3F);
- outU8Array[outIdx++] = 0x80 | (u & 0x3F);
- }
- }
- return outU8Array;
- }
-}
diff --git a/test/utils/TextDecoderPolyfill-test.js b/test/utils/TextDecoderPolyfill-test.js
deleted file mode 100644
index 84f5edf187..0000000000
--- a/test/utils/TextDecoderPolyfill-test.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-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.
-*/
-
-"use strict";
-
-import TextDecoderPolyfill from 'utils/TextDecoderPolyfill';
-
-import * as testUtils from '../test-utils';
-import expect from 'expect';
-
-describe('textDecoderPolyfill', function() {
- beforeEach(function() {
- testUtils.beforeEach(this);
- });
-
- it('should correctly decode a range of strings', function() {
- const decoder = new TextDecoderPolyfill();
-
- expect(decoder.decode(Uint8Array.of(65, 66, 67))).toEqual('ABC');
- expect(decoder.decode(Uint8Array.of(0xC3, 0xA6))).toEqual('æ');
- expect(decoder.decode(Uint8Array.of(0xE2, 0x82, 0xAC))).toEqual('€');
- expect(decoder.decode(Uint8Array.of(0xF0, 0x9F, 0x92, 0xA9))).toEqual('\uD83D\uDCA9');
- });
-
- it('should ignore byte-order marks', function() {
- const decoder = new TextDecoderPolyfill();
- expect(decoder.decode(Uint8Array.of(0xEF, 0xBB, 0xBF, 65)))
- .toEqual('A');
- });
-
- it('should not ignore byte-order marks in the middle of the array', function() {
- const decoder = new TextDecoderPolyfill();
- expect(decoder.decode(Uint8Array.of(65, 0xEF, 0xBB, 0xBF, 66)))
- .toEqual('A\uFEFFB');
- });
-
- it('should reject overlong encodings', function() {
- const decoder = new TextDecoderPolyfill();
-
- // euro, as 4 bytes
- expect(decoder.decode(Uint8Array.of(65, 0xF0, 0x82, 0x82, 0xAC, 67)))
- .toEqual('A\uFFFD\uFFFD\uFFFD\uFFFDC');
- });
-
- it('should reject 5 and 6-byte encodings', function() {
- const decoder = new TextDecoderPolyfill();
-
- expect(decoder.decode(Uint8Array.of(65, 0xF8, 0x82, 0x82, 0x82, 0x82, 67)))
- .toEqual('A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDC');
- });
-
- it('should reject code points beyond 0x10000', function() {
- const decoder = new TextDecoderPolyfill();
-
- expect(decoder.decode(Uint8Array.of(0xF4, 0xA0, 0x80, 0x80)))
- .toEqual('\uFFFD\uFFFD\uFFFD\uFFFD');
- });
-
- it('should cope with end-of-string', function() {
- const decoder = new TextDecoderPolyfill();
-
- expect(decoder.decode(Uint8Array.of(65, 0xC3)))
- .toEqual('A\uFFFD');
-
- expect(decoder.decode(Uint8Array.of(65, 0xE2, 0x82)))
- .toEqual('A\uFFFD\uFFFD');
-
- expect(decoder.decode(Uint8Array.of(65, 0xF0, 0x9F, 0x92)))
- .toEqual('A\uFFFD\uFFFD\uFFFD');
- });
-
-});
diff --git a/test/utils/TextEncoderPolyfill-test.js b/test/utils/TextEncoderPolyfill-test.js
deleted file mode 100644
index 4f422ec375..0000000000
--- a/test/utils/TextEncoderPolyfill-test.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-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.
-*/
-
-"use strict";
-
-import TextEncoderPolyfill from 'utils/TextEncoderPolyfill';
-
-import * as testUtils from '../test-utils';
-import expect from 'expect';
-
-describe('textEncoderPolyfill', function() {
- beforeEach(function() {
- testUtils.beforeEach(this);
- });
-
- it('should correctly encode a range of strings', function() {
- const encoder = new TextEncoderPolyfill();
-
- expect(encoder.encode('ABC')).toEqual(Uint8Array.of(65, 66, 67));
- expect(encoder.encode('æ')).toEqual(Uint8Array.of(0xC3, 0xA6));
- expect(encoder.encode('€')).toEqual(Uint8Array.of(0xE2, 0x82, 0xAC));
-
- // PILE OF POO (💩)
- expect(encoder.encode('\uD83D\uDCA9')).toEqual(Uint8Array.of(0xF0, 0x9F, 0x92, 0xA9));
- });
-});