UNPKG

matrix-js-sdk

Version:

Matrix Client-Server SDK for Javascript

329 lines (271 loc) 11.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Backend = exports.VERSION = undefined; var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); exports.upgradeDatabase = upgradeDatabase; var _bluebird = require('bluebird'); var _bluebird2 = _interopRequireDefault(_bluebird); var _utils = require('../../utils'); var _utils2 = _interopRequireDefault(_utils); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var VERSION = exports.VERSION = 1; /** * Implementation of a CryptoStore which is backed by an existing * IndexedDB connection. Generally you want IndexedDBCryptoStore * which connects to the database and defers to one of these. * * @implements {module:crypto/store/base~CryptoStore} */ var Backend = exports.Backend = function () { /** * @param {IDBDatabase} db */ function Backend(db) { var _this = this; (0, _classCallCheck3.default)(this, Backend); this._db = db; // make sure we close the db on `onversionchange` - otherwise // attempts to delete the database will block (and subsequent // attempts to re-create it will also block). db.onversionchange = function (ev) { console.log('versionchange for indexeddb ' + _this._dbName + ': closing'); db.close(); }; } /** * Look for an existing outgoing room key request, and if none is found, * add a new one * * @param {module:crypto/store/base~OutgoingRoomKeyRequest} request * * @returns {Promise} resolves to * {@link module:crypto/store/base~OutgoingRoomKeyRequest}: either the * same instance as passed in, or the existing one. */ (0, _createClass3.default)(Backend, [{ key: 'getOrAddOutgoingRoomKeyRequest', value: function getOrAddOutgoingRoomKeyRequest(request) { var requestBody = request.requestBody; var deferred = _bluebird2.default.defer(); var txn = this._db.transaction("outgoingRoomKeyRequests", "readwrite"); txn.onerror = deferred.reject; // first see if we already have an entry for this request. this._getOutgoingRoomKeyRequest(txn, requestBody, function (existing) { if (existing) { // this entry matches the request - return it. console.log('already have key request outstanding for ' + (requestBody.room_id + ' / ' + requestBody.session_id + ': ') + 'not sending another'); deferred.resolve(existing); return; } // we got to the end of the list without finding a match // - add the new request. console.log('enqueueing key request for ' + requestBody.room_id + ' / ' + requestBody.session_id); var store = txn.objectStore("outgoingRoomKeyRequests"); store.add(request); txn.onsuccess = function () { deferred.resolve(request); }; }); return deferred.promise; } /** * Look for an existing room key request * * @param {module:crypto~RoomKeyRequestBody} requestBody * existing request to look for * * @return {Promise} resolves to the matching * {@link module:crypto/store/base~OutgoingRoomKeyRequest}, or null if * not found */ }, { key: 'getOutgoingRoomKeyRequest', value: function getOutgoingRoomKeyRequest(requestBody) { var deferred = _bluebird2.default.defer(); var txn = this._db.transaction("outgoingRoomKeyRequests", "readonly"); txn.onerror = deferred.reject; this._getOutgoingRoomKeyRequest(txn, requestBody, function (existing) { deferred.resolve(existing); }); return deferred.promise; } /** * look for an existing room key request in the db * * @private * @param {IDBTransaction} txn database transaction * @param {module:crypto~RoomKeyRequestBody} requestBody * existing request to look for * @param {Function} callback function to call with the results of the * search. Either passed a matching * {@link module:crypto/store/base~OutgoingRoomKeyRequest}, or null if * not found. */ }, { key: '_getOutgoingRoomKeyRequest', value: function _getOutgoingRoomKeyRequest(txn, requestBody, callback) { var store = txn.objectStore("outgoingRoomKeyRequests"); var idx = store.index("session"); var cursorReq = idx.openCursor([requestBody.room_id, requestBody.session_id]); cursorReq.onsuccess = function (ev) { var cursor = ev.target.result; if (!cursor) { // no match found callback(null); return; } var existing = cursor.value; if (_utils2.default.deepCompare(existing.requestBody, requestBody)) { // got a match callback(existing); return; } // look at the next entry in the index cursor.continue(); }; } /** * Look for room key requests by state * * @param {Array<Number>} wantedStates list of acceptable states * * @return {Promise} resolves to the a * {@link module:crypto/store/base~OutgoingRoomKeyRequest}, or null if * there are no pending requests in those states. If there are multiple * requests in those states, an arbitrary one is chosen. */ }, { key: 'getOutgoingRoomKeyRequestByState', value: function getOutgoingRoomKeyRequestByState(wantedStates) { if (wantedStates.length === 0) { return _bluebird2.default.resolve(null); } // this is a bit tortuous because we need to make sure we do the lookup // in a single transaction, to avoid having a race with the insertion // code. // index into the wantedStates array var stateIndex = 0; var result = void 0; function onsuccess(ev) { var cursor = ev.target.result; if (cursor) { // got a match result = cursor.value; return; } // try the next state in the list stateIndex++; if (stateIndex >= wantedStates.length) { // no matches return; } var wantedState = wantedStates[stateIndex]; var cursorReq = ev.target.source.openCursor(wantedState); cursorReq.onsuccess = onsuccess; } var txn = this._db.transaction("outgoingRoomKeyRequests", "readonly"); var store = txn.objectStore("outgoingRoomKeyRequests"); var wantedState = wantedStates[stateIndex]; var cursorReq = store.index("state").openCursor(wantedState); cursorReq.onsuccess = onsuccess; return promiseifyTxn(txn).then(function () { return result; }); } /** * Look for an existing room key request by id and state, and update it if * found * * @param {string} requestId ID of request to update * @param {number} expectedState state we expect to find the request in * @param {Object} updates name/value map of updates to apply * * @returns {Promise} resolves to * {@link module:crypto/store/base~OutgoingRoomKeyRequest} * updated request, or null if no matching row was found */ }, { key: 'updateOutgoingRoomKeyRequest', value: function updateOutgoingRoomKeyRequest(requestId, expectedState, updates) { var result = null; function onsuccess(ev) { var cursor = ev.target.result; if (!cursor) { return; } var data = cursor.value; if (data.state != expectedState) { console.warn('Cannot update room key request from ' + expectedState + ' ' + ('as it was already updated to ' + data.state)); return; } (0, _assign2.default)(data, updates); cursor.update(data); result = data; } var txn = this._db.transaction("outgoingRoomKeyRequests", "readwrite"); var cursorReq = txn.objectStore("outgoingRoomKeyRequests").openCursor(requestId); cursorReq.onsuccess = onsuccess; return promiseifyTxn(txn).then(function () { return result; }); } /** * Look for an existing room key request by id and state, and delete it if * found * * @param {string} requestId ID of request to update * @param {number} expectedState state we expect to find the request in * * @returns {Promise} resolves once the operation is completed */ }, { key: 'deleteOutgoingRoomKeyRequest', value: function deleteOutgoingRoomKeyRequest(requestId, expectedState) { var txn = this._db.transaction("outgoingRoomKeyRequests", "readwrite"); var cursorReq = txn.objectStore("outgoingRoomKeyRequests").openCursor(requestId); cursorReq.onsuccess = function (ev) { var cursor = ev.target.result; if (!cursor) { return; } var data = cursor.value; if (data.state != expectedState) { console.warn('Cannot delete room key request in state ' + data.state + ' ' + ('(expected ' + expectedState + ')')); return; } cursor.delete(); }; return promiseifyTxn(txn); } }]); return Backend; }(); function upgradeDatabase(db, oldVersion) { console.log('Upgrading IndexedDBCryptoStore from version ' + oldVersion + (' to ' + VERSION)); if (oldVersion < 1) { // The database did not previously exist. createDatabase(db); } // Expand as needed. } function createDatabase(db) { var outgoingRoomKeyRequestsStore = db.createObjectStore("outgoingRoomKeyRequests", { keyPath: "requestId" }); // we assume that the RoomKeyRequestBody will have room_id and session_id // properties, to make the index efficient. outgoingRoomKeyRequestsStore.createIndex("session", ["requestBody.room_id", "requestBody.session_id"]); outgoingRoomKeyRequestsStore.createIndex("state", "state"); } function promiseifyTxn(txn) { return new _bluebird2.default(function (resolve, reject) { txn.oncomplete = resolve; txn.onerror = reject; }); } //# sourceMappingURL=indexeddb-crypto-store-backend.js.map