mirror of https://github.com/vector-im/riot-web
Use text-encoding-utf-8 as a TextEncoder polyfill
Somebody else seems to have done a good job of polyfilling TextEncoder, so let's use that.pull/21833/head
parent
09ce74cc76
commit
31df78f946
|
@ -67,6 +67,7 @@
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||||
"sanitize-html": "^1.11.1",
|
"sanitize-html": "^1.11.1",
|
||||||
|
"text-encoding-utf-8": "^1.0.1",
|
||||||
"velocity-vector": "vector-im/velocity#059e3b2",
|
"velocity-vector": "vector-im/velocity#059e3b2",
|
||||||
"whatwg-fetch": "^1.0.0"
|
"whatwg-fetch": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 = (
|
||||||
|
<div className="mx_Dialog_content">
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
<form onSubmit={this._onPassphraseFormSubmit}>
|
||||||
|
<div className="mx_TextInputDialog_label">
|
||||||
|
<label htmlFor="passphrase1">Enter passphrase</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input ref="passphrase1" id="passphrase1"
|
||||||
|
className="mx_TextInputDialog_input"
|
||||||
|
autoFocus={true} size="64" type="password"/>
|
||||||
|
</div>
|
||||||
|
<div className="mx_Dialog_buttons">
|
||||||
|
<input className="mx_Dialog_primary" type="submit" value="Export" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_exportE2eKeysDialog">
|
||||||
|
<div className="mx_Dialog_title">
|
||||||
|
Export room keys
|
||||||
|
</div>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -17,13 +17,14 @@ limitations under the License.
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// polyfill textencoder if necessary
|
// polyfill textencoder if necessary
|
||||||
|
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
|
||||||
let TextEncoder = window.TextEncoder;
|
let TextEncoder = window.TextEncoder;
|
||||||
if (!TextEncoder) {
|
if (!TextEncoder) {
|
||||||
TextEncoder = require('./TextEncoderPolyfill');
|
TextEncoder = TextEncodingUtf8.TextEncoder;
|
||||||
}
|
}
|
||||||
let TextDecoder = window.TextDecoder;
|
let TextDecoder = window.TextDecoder;
|
||||||
if (TextDecoder) {
|
if (!TextDecoder) {
|
||||||
TextDecoder = require('./TextDecoderPolyfill');
|
TextDecoder = TextEncodingUtf8.TextDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle;
|
const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue