trezor-link
Version:
Trezor Link for browser
593 lines (495 loc) • 18.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _desc, _value, _class;
var _defered = require('../defered');
var _debugDecorator = require('../debug-decorator');
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
var pingBuffer = new TextEncoder().encode('PINGPING').buffer;
var pongBuffer = new TextEncoder().encode('PONGPONG').buffer;
// This is overcomplicated and could probable be simplier
var ChromeUdpPlugin = (_class = function () {
function ChromeUdpPlugin(portDiff) {
_classCallCheck(this, ChromeUdpPlugin);
this.name = 'ChromeUdpPlugin';
this.waiting = {};
this.buffered = {};
this.infos = {};
this.sockets = {};
this.version = "0.2.109";
this.debug = false;
this.allowsWriteAndEnumerate = true;
this.ports = [];
this.requestNeeded = false;
if (portDiff == null) {
this.portDiff = 3;
} else {
this.portDiff = portDiff;
}
}
// state of connections
// for streaming of reading results
_createClass(ChromeUdpPlugin, [{
key: 'init',
value: function init(debug) {
var _this = this;
this.debug = !!debug;
try {
chrome.sockets.udp.onReceive.addListener(function (_ref) {
var socketId = _ref.socketId,
data = _ref.data;
_this._udpListener(socketId, data);
});
return Promise.resolve();
} catch (e) {
// if not Chrome, not sockets etc, this will reject
return Promise.reject(e);
}
}
}, {
key: 'setPorts',
value: function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(ports) {
var oldPorts, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, port, _socket, socket2;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!(ports.length > this.portDiff)) {
_context.next = 2;
break;
}
throw new Error('Too many ports. Max ' + this.portDiff + ' allowed.');
case 2:
oldPorts = this.ports;
_iteratorNormalCompletion = true;
_didIteratorError = false;
_iteratorError = undefined;
_context.prev = 6;
_iterator = oldPorts[Symbol.iterator]();
case 8:
if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
_context.next = 21;
break;
}
port = _step.value;
_socket = this.sockets[(port + this.portDiff).toString()];
if (!_socket) {
_context.next = 14;
break;
}
_context.next = 14;
return this._udpDisconnect(parseInt(_socket));
case 14:
socket2 = this.sockets[(port + this.portDiff * 2).toString()];
if (!socket2) {
_context.next = 18;
break;
}
_context.next = 18;
return this._udpDisconnect(parseInt(socket2));
case 18:
_iteratorNormalCompletion = true;
_context.next = 8;
break;
case 21:
_context.next = 27;
break;
case 23:
_context.prev = 23;
_context.t0 = _context['catch'](6);
_didIteratorError = true;
_iteratorError = _context.t0;
case 27:
_context.prev = 27;
_context.prev = 28;
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
case 30:
_context.prev = 30;
if (!_didIteratorError) {
_context.next = 33;
break;
}
throw _iteratorError;
case 33:
return _context.finish(30);
case 34:
return _context.finish(27);
case 35:
this.ports = ports;
case 36:
case 'end':
return _context.stop();
}
}
}, _callee, this, [[6, 23, 27, 35], [28,, 30, 34]]);
}));
function setPorts(_x) {
return _ref2.apply(this, arguments);
}
return setPorts;
}()
}, {
key: 'enumerate',
value: function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
var res, wrongBufferError, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, port, _socket2, _portIn, socketS, resBuffer, _socket3;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
res = [];
wrongBufferError = new Error();
_iteratorNormalCompletion2 = true;
_didIteratorError2 = false;
_iteratorError2 = undefined;
_context2.prev = 5;
_iterator2 = this.ports[Symbol.iterator]();
case 7:
if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) {
_context2.next = 45;
break;
}
port = _step2.value;
_context2.prev = 9;
_socket2 = void 0;
_portIn = (port + this.portDiff).toString();
socketS = this.sockets[_portIn];
if (!socketS) {
_context2.next = 17;
break;
}
_socket2 = parseInt(socketS);
_context2.next = 20;
break;
case 17:
_context2.next = 19;
return this._udpConnect(port, this.portDiff);
case 19:
_socket2 = _context2.sent;
case 20:
_context2.prev = 20;
_context2.next = 23;
return this._udpLowSend(_socket2, pingBuffer);
case 23:
_context2.next = 25;
return Promise.race([(0, _defered.rejectTimeoutPromise)(1000, wrongBufferError), this._udpLowReceive(_socket2, 80)] // 80 is "P" in "PONG"
);
case 25:
resBuffer = _context2.sent;
if (arraybufferEqual(pongBuffer, resBuffer)) {
_context2.next = 28;
break;
}
throw wrongBufferError;
case 28:
res.push({ path: port.toString() });
_context2.next = 38;
break;
case 31:
_context2.prev = 31;
_context2.t0 = _context2['catch'](20);
if (!(_context2.t0 === wrongBufferError)) {
_context2.next = 38;
break;
}
_socket3 = this.sockets[(port + this.portDiff).toString()];
if (!_socket3) {
_context2.next = 38;
break;
}
_context2.next = 38;
return this._udpDisconnect(parseInt(_socket3));
case 38:
_context2.next = 42;
break;
case 40:
_context2.prev = 40;
_context2.t1 = _context2['catch'](9);
case 42:
_iteratorNormalCompletion2 = true;
_context2.next = 7;
break;
case 45:
_context2.next = 51;
break;
case 47:
_context2.prev = 47;
_context2.t2 = _context2['catch'](5);
_didIteratorError2 = true;
_iteratorError2 = _context2.t2;
case 51:
_context2.prev = 51;
_context2.prev = 52;
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
case 54:
_context2.prev = 54;
if (!_didIteratorError2) {
_context2.next = 57;
break;
}
throw _iteratorError2;
case 57:
return _context2.finish(54);
case 58:
return _context2.finish(51);
case 59:
return _context2.abrupt('return', res);
case 60:
case 'end':
return _context2.stop();
}
}
}, _callee2, this, [[5, 47, 51, 59], [9, 40], [20, 31], [52,, 54, 58]]);
}));
function enumerate() {
return _ref3.apply(this, arguments);
}
return enumerate;
}()
}, {
key: 'send',
value: function send(device, session, data) {
var socket = parseInt(session);
if (isNaN(socket)) {
return Promise.reject(new Error('Session not a number'));
}
return this._udpSend(socket, data);
}
}, {
key: 'receive',
value: function receive(device, session) {
var socket = parseInt(session);
if (isNaN(socket)) {
return Promise.reject(new Error('Session not a number'));
}
return this._udpReceive(socket);
}
}, {
key: 'connect',
value: function connect(device) {
var port = parseInt(device);
if (isNaN(port)) {
return Promise.reject(new Error('Device not a number'));
}
var socket = this.sockets[(port + this.portDiff).toString()];
if (socket == null) {
return Promise.reject(new Error('Unknown device ' + device));
}
return Promise.resolve(socket);
}
}, {
key: 'disconnect',
value: function disconnect(path, session) {
var socket = parseInt(session);
if (isNaN(socket)) {
return Promise.reject(new Error('Session not a number'));
}
return Promise.resolve();
}
}, {
key: '_udpDisconnect',
value: function _udpDisconnect(socketId) {
var _this2 = this;
return new Promise(function (resolve, reject) {
try {
chrome.sockets.udp.close(socketId, function () {
if (chrome.runtime.lastError) {
reject(new Error('Disconnect error ' + JSON.stringify(chrome.runtime.lastError)));
} else {
var info = _this2.infos[socketId.toString()];
delete _this2.infos[socketId.toString()];
if (info != null) {
delete _this2.sockets[info.portIn.toString()];
}
resolve();
}
});
} catch (e) {
reject(e);
}
});
}
}, {
key: '_udpConnect',
value: function _udpConnect(port, diff) {
var _this3 = this;
var address = '127.0.0.1';
return new Promise(function (resolve, reject) {
try {
chrome.sockets.udp.create({}, function (_ref4) {
var socketId = _ref4.socketId;
if (chrome.runtime.lastError) {
reject(new Error('Connect error ' + JSON.stringify(chrome.runtime.lastError)));
} else {
try {
chrome.sockets.udp.bind(socketId, '127.0.0.1', port + diff, function (result) {
if (chrome.runtime.lastError) {
reject(new Error('Bind error ' + JSON.stringify(chrome.runtime.lastError)));
} else {
if (result >= 0) {
var _portIn2 = port + diff;
_this3.infos[socketId.toString()] = { address: address, portOut: port, portIn: _portIn2 };
_this3.sockets[_portIn2.toString()] = socketId.toString();
resolve(socketId);
} else {
reject('Cannot create socket, error: ' + result);
}
}
});
} catch (e) {
reject(e);
}
}
});
} catch (e) {
reject(e);
}
});
}
}, {
key: '_udpReceive',
value: function _udpReceive(socketId) {
return this._udpLowReceive(socketId, 63).then(function (data) {
var dataView = new Uint8Array(data);
if (dataView[0] !== 63) {
throw new Error('Invalid data; first byte should be 63, is ' + dataView[0]);
}
return data.slice(1);
});
}
}, {
key: '_udpLowReceive',
value: function _udpLowReceive(socketId, type) {
var id = socketId.toString();
var socketPlusType = id + '-' + type;
if (this.buffered[socketPlusType] != null) {
var res = this.buffered[socketPlusType].shift();
if (this.buffered[socketPlusType].length === 0) {
delete this.buffered[socketPlusType];
}
return Promise.resolve(res);
}
if (this.waiting[socketPlusType] != null) {
return Promise.reject('Something else already listening on socketId ' + socketPlusType);
}
var d = (0, _defered.create)();
this.waiting[socketPlusType] = d;
return d.promise;
}
}, {
key: '_udpSend',
value: function _udpSend(socketId, data) {
var sendDataV = new Uint8Array(64);
sendDataV[0] = 63;
sendDataV.set(new Uint8Array(data), 1);
var sendData = sendDataV.buffer;
return this._udpLowSend(socketId, sendData);
}
}, {
key: '_udpLowSend',
value: function _udpLowSend(socketId, sendData) {
var id = socketId.toString();
var info = this.infos[id];
if (info == null) {
return Promise.reject('Socket ' + socketId + ' does not exist');
}
return new Promise(function (resolve, reject) {
try {
chrome.sockets.udp.send(socketId, sendData, info.address, info.portOut, function (_ref5) {
var resultCode = _ref5.resultCode;
if (chrome.runtime.lastError) {
reject(new Error('Send error ' + JSON.stringify(chrome.runtime.lastError)));
} else {
if (resultCode >= 0) {
resolve();
} else {
reject('Cannot send, error: ' + resultCode);
}
}
});
} catch (e) {
reject(e);
}
});
}
}, {
key: '_udpListener',
value: function _udpListener(socketId, data) {
var id = socketId.toString();
var dataArr = new Uint8Array(data);
var type = dataArr[0];
var socketPlusType = id + '-' + type;
var d = this.waiting[socketPlusType];
if (d != null) {
d.resolve(data);
delete this.waiting[socketPlusType];
} else {
if (this.infos[socketPlusType] != null) {
if (this.buffered[socketPlusType] == null) {
this.buffered[socketPlusType] = [];
}
this.buffered[socketPlusType].push(data);
}
}
}
}, {
key: 'requestDevice',
value: function requestDevice() {
return Promise.reject();
}
}]);
return ChromeUdpPlugin;
}(), (_applyDecoratedDescriptor(_class.prototype, 'init', [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, 'init'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'connect', [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, 'connect'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'disconnect', [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, 'disconnect'), _class.prototype)), _class);
// from https://github.com/wbinnssmith/arraybuffer-equal
// (c) 2015 Will Binns-Smith. Licensed MIT.
exports.default = ChromeUdpPlugin;
function arraybufferEqual(buf1, buf2) {
if (buf1 === buf2) {
return true;
}
if (buf1.byteLength !== buf2.byteLength) {
return false;
}
var view1 = new DataView(buf1);
var view2 = new DataView(buf2);
var i = buf1.byteLength;
while (i--) {
if (view1.getUint8(i) !== view2.getUint8(i)) {
return false;
}
}
return true;
}
module.exports = exports['default'];