'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _values = require('babel-runtime/core-js/object/values');

var _values2 = _interopRequireDefault(_values);

var _keys = require('babel-runtime/core-js/object/keys');

var _keys2 = _interopRequireDefault(_keys);

var _assign = require('babel-runtime/core-js/object/assign');

var _assign2 = _interopRequireDefault(_assign);

var _getIterator2 = require('babel-runtime/core-js/get-iterator');

var _getIterator3 = _interopRequireDefault(_getIterator2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _bluebird = require('bluebird');

var _bluebird2 = _interopRequireDefault(_bluebird);

var _logger = require('../../logger');

var _logger2 = _interopRequireDefault(_logger);

var _utils = require('../../utils');

var _utils2 = _interopRequireDefault(_utils);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Internal module. in-memory storage for e2e.
 *
 * @module
 */

/**
 * @implements {module:crypto/store/base~CryptoStore}
 */
var MemoryCryptoStore = function () {
    function MemoryCryptoStore() {
        (0, _classCallCheck3.default)(this, MemoryCryptoStore);

        this._outgoingRoomKeyRequests = [];
        this._account = null;

        // Map of {devicekey -> {sessionId -> session pickle}}
        this._sessions = {};
        // Map of {senderCurve25519Key+'/'+sessionId -> session data object}
        this._inboundGroupSessions = {};
        // Opaque device data object
        this._deviceData = null;
        // roomId -> Opaque roomInfo object
        this._rooms = {};
        // Set of {senderCurve25519Key+'/'+sessionId}
        this._sessionsNeedingBackup = {};
    }

    /**
     * Delete all data from this store.
     *
     * @returns {Promise} Promise which resolves when the store has been cleared.
     */


    (0, _createClass3.default)(MemoryCryptoStore, [{
        key: 'deleteAllData',
        value: function deleteAllData() {
            return _bluebird2.default.resolve();
        }

        /**
         * 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.
         */

    }, {
        key: 'getOrAddOutgoingRoomKeyRequest',
        value: function getOrAddOutgoingRoomKeyRequest(request) {
            var _this = this;

            var requestBody = request.requestBody;

            return _bluebird2.default.try(function () {
                // first see if we already have an entry for this request.
                var existing = _this._getOutgoingRoomKeyRequest(requestBody);

                if (existing) {
                    // this entry matches the request - return it.
                    _logger2.default.log('already have key request outstanding for ' + (requestBody.room_id + ' / ' + requestBody.session_id + ': ') + 'not sending another');
                    return existing;
                }

                // we got to the end of the list without finding a match
                // - add the new request.
                _logger2.default.log('enqueueing key request for ' + requestBody.room_id + ' / ' + requestBody.session_id);
                _this._outgoingRoomKeyRequests.push(request);
                return request;
            });
        }

        /**
         * 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) {
            return _bluebird2.default.resolve(this._getOutgoingRoomKeyRequest(requestBody));
        }

        /**
         * Looks for existing room key request, and returns the result synchronously.
         *
         * @internal
         *
         * @param {module:crypto~RoomKeyRequestBody} requestBody
         *    existing request to look for
         *
         * @return {module:crypto/store/base~OutgoingRoomKeyRequest?}
         *    the matching request, or null if not found
         */

    }, {
        key: '_getOutgoingRoomKeyRequest',
        value: function _getOutgoingRoomKeyRequest(requestBody) {
            var _iteratorNormalCompletion = true;
            var _didIteratorError = false;
            var _iteratorError = undefined;

            try {
                for (var _iterator = (0, _getIterator3.default)(this._outgoingRoomKeyRequests), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                    var existing = _step.value;

                    if (_utils2.default.deepCompare(existing.requestBody, requestBody)) {
                        return existing;
                    }
                }
            } catch (err) {
                _didIteratorError = true;
                _iteratorError = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion && _iterator.return) {
                        _iterator.return();
                    }
                } finally {
                    if (_didIteratorError) {
                        throw _iteratorError;
                    }
                }
            }

            return null;
        }

        /**
         * 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
         */

    }, {
        key: 'getOutgoingRoomKeyRequestByState',
        value: function getOutgoingRoomKeyRequestByState(wantedStates) {
            var _iteratorNormalCompletion2 = true;
            var _didIteratorError2 = false;
            var _iteratorError2 = undefined;

            try {
                for (var _iterator2 = (0, _getIterator3.default)(this._outgoingRoomKeyRequests), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                    var req = _step2.value;
                    var _iteratorNormalCompletion3 = true;
                    var _didIteratorError3 = false;
                    var _iteratorError3 = undefined;

                    try {
                        for (var _iterator3 = (0, _getIterator3.default)(wantedStates), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
                            var state = _step3.value;

                            if (req.state === state) {
                                return _bluebird2.default.resolve(req);
                            }
                        }
                    } catch (err) {
                        _didIteratorError3 = true;
                        _iteratorError3 = err;
                    } finally {
                        try {
                            if (!_iteratorNormalCompletion3 && _iterator3.return) {
                                _iterator3.return();
                            }
                        } finally {
                            if (_didIteratorError3) {
                                throw _iteratorError3;
                            }
                        }
                    }
                }
            } catch (err) {
                _didIteratorError2 = true;
                _iteratorError2 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion2 && _iterator2.return) {
                        _iterator2.return();
                    }
                } finally {
                    if (_didIteratorError2) {
                        throw _iteratorError2;
                    }
                }
            }

            return _bluebird2.default.resolve(null);
        }
    }, {
        key: 'getOutgoingRoomKeyRequestsByTarget',
        value: function getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates) {
            var results = [];

            var _iteratorNormalCompletion4 = true;
            var _didIteratorError4 = false;
            var _iteratorError4 = undefined;

            try {
                for (var _iterator4 = (0, _getIterator3.default)(this._outgoingRoomKeyRequests), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
                    var req = _step4.value;
                    var _iteratorNormalCompletion5 = true;
                    var _didIteratorError5 = false;
                    var _iteratorError5 = undefined;

                    try {
                        for (var _iterator5 = (0, _getIterator3.default)(wantedStates), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
                            var state = _step5.value;

                            if (req.state === state && req.recipients.includes({ userId: userId, deviceId: deviceId })) {
                                results.push(req);
                            }
                        }
                    } catch (err) {
                        _didIteratorError5 = true;
                        _iteratorError5 = err;
                    } finally {
                        try {
                            if (!_iteratorNormalCompletion5 && _iterator5.return) {
                                _iterator5.return();
                            }
                        } finally {
                            if (_didIteratorError5) {
                                throw _iteratorError5;
                            }
                        }
                    }
                }
            } catch (err) {
                _didIteratorError4 = true;
                _iteratorError4 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion4 && _iterator4.return) {
                        _iterator4.return();
                    }
                } finally {
                    if (_didIteratorError4) {
                        throw _iteratorError4;
                    }
                }
            }

            return _bluebird2.default.resolve(results);
        }

        /**
         * 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 _iteratorNormalCompletion6 = true;
            var _didIteratorError6 = false;
            var _iteratorError6 = undefined;

            try {
                for (var _iterator6 = (0, _getIterator3.default)(this._outgoingRoomKeyRequests), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
                    var req = _step6.value;

                    if (req.requestId !== requestId) {
                        continue;
                    }

                    if (req.state != expectedState) {
                        _logger2.default.warn('Cannot update room key request from ' + expectedState + ' ' + ('as it was already updated to ' + req.state));
                        return _bluebird2.default.resolve(null);
                    }
                    (0, _assign2.default)(req, updates);
                    return _bluebird2.default.resolve(req);
                }
            } catch (err) {
                _didIteratorError6 = true;
                _iteratorError6 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion6 && _iterator6.return) {
                        _iterator6.return();
                    }
                } finally {
                    if (_didIteratorError6) {
                        throw _iteratorError6;
                    }
                }
            }

            return _bluebird2.default.resolve(null);
        }

        /**
         * 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) {
            for (var i = 0; i < this._outgoingRoomKeyRequests.length; i++) {
                var req = this._outgoingRoomKeyRequests[i];

                if (req.requestId !== requestId) {
                    continue;
                }

                if (req.state != expectedState) {
                    _logger2.default.warn('Cannot delete room key request in state ' + req.state + ' ' + ('(expected ' + expectedState + ')'));
                    return _bluebird2.default.resolve(null);
                }

                this._outgoingRoomKeyRequests.splice(i, 1);
                return _bluebird2.default.resolve(req);
            }

            return _bluebird2.default.resolve(null);
        }

        // Olm Account

    }, {
        key: 'getAccount',
        value: function getAccount(txn, func) {
            func(this._account);
        }
    }, {
        key: 'storeAccount',
        value: function storeAccount(txn, newData) {
            this._account = newData;
        }

        // Olm Sessions

    }, {
        key: 'countEndToEndSessions',
        value: function countEndToEndSessions(txn, func) {
            return (0, _keys2.default)(this._sessions).length;
        }
    }, {
        key: 'getEndToEndSession',
        value: function getEndToEndSession(deviceKey, sessionId, txn, func) {
            var deviceSessions = this._sessions[deviceKey] || {};
            func(deviceSessions[sessionId] || null);
        }
    }, {
        key: 'getEndToEndSessions',
        value: function getEndToEndSessions(deviceKey, txn, func) {
            func(this._sessions[deviceKey] || {});
        }
    }, {
        key: 'getAllEndToEndSessions',
        value: function getAllEndToEndSessions(txn, func) {
            var _iteratorNormalCompletion7 = true;
            var _didIteratorError7 = false;
            var _iteratorError7 = undefined;

            try {
                for (var _iterator7 = (0, _getIterator3.default)((0, _values2.default)(this._sessions)), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
                    var deviceSessions = _step7.value;
                    var _iteratorNormalCompletion8 = true;
                    var _didIteratorError8 = false;
                    var _iteratorError8 = undefined;

                    try {
                        for (var _iterator8 = (0, _getIterator3.default)((0, _values2.default)(deviceSessions)), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
                            var sess = _step8.value;

                            func(sess);
                        }
                    } catch (err) {
                        _didIteratorError8 = true;
                        _iteratorError8 = err;
                    } finally {
                        try {
                            if (!_iteratorNormalCompletion8 && _iterator8.return) {
                                _iterator8.return();
                            }
                        } finally {
                            if (_didIteratorError8) {
                                throw _iteratorError8;
                            }
                        }
                    }
                }
            } catch (err) {
                _didIteratorError7 = true;
                _iteratorError7 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion7 && _iterator7.return) {
                        _iterator7.return();
                    }
                } finally {
                    if (_didIteratorError7) {
                        throw _iteratorError7;
                    }
                }
            }
        }
    }, {
        key: 'storeEndToEndSession',
        value: function storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) {
            var deviceSessions = this._sessions[deviceKey];
            if (deviceSessions === undefined) {
                deviceSessions = {};
                this._sessions[deviceKey] = deviceSessions;
            }
            deviceSessions[sessionId] = sessionInfo;
        }

        // Inbound Group Sessions

    }, {
        key: 'getEndToEndInboundGroupSession',
        value: function getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
            func(this._inboundGroupSessions[senderCurve25519Key + '/' + sessionId] || null);
        }
    }, {
        key: 'getAllEndToEndInboundGroupSessions',
        value: function getAllEndToEndInboundGroupSessions(txn, func) {
            var _iteratorNormalCompletion9 = true;
            var _didIteratorError9 = false;
            var _iteratorError9 = undefined;

            try {
                for (var _iterator9 = (0, _getIterator3.default)((0, _keys2.default)(this._inboundGroupSessions)), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
                    var key = _step9.value;

                    // we can't use split, as the components we are trying to split out
                    // might themselves contain '/' characters. We rely on the
                    // senderKey being a (32-byte) curve25519 key, base64-encoded
                    // (hence 43 characters long).

                    func({
                        senderKey: key.substr(0, 43),
                        sessionId: key.substr(44),
                        sessionData: this._inboundGroupSessions[key]
                    });
                }
            } catch (err) {
                _didIteratorError9 = true;
                _iteratorError9 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion9 && _iterator9.return) {
                        _iterator9.return();
                    }
                } finally {
                    if (_didIteratorError9) {
                        throw _iteratorError9;
                    }
                }
            }

            func(null);
        }
    }, {
        key: 'addEndToEndInboundGroupSession',
        value: function addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
            var k = senderCurve25519Key + '/' + sessionId;
            if (this._inboundGroupSessions[k] === undefined) {
                this._inboundGroupSessions[k] = sessionData;
            }
        }
    }, {
        key: 'storeEndToEndInboundGroupSession',
        value: function storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
            this._inboundGroupSessions[senderCurve25519Key + '/' + sessionId] = sessionData;
        }

        // Device Data

    }, {
        key: 'getEndToEndDeviceData',
        value: function getEndToEndDeviceData(txn, func) {
            func(this._deviceData);
        }
    }, {
        key: 'storeEndToEndDeviceData',
        value: function storeEndToEndDeviceData(deviceData, txn) {
            this._deviceData = deviceData;
        }

        // E2E rooms

    }, {
        key: 'storeEndToEndRoom',
        value: function storeEndToEndRoom(roomId, roomInfo, txn) {
            this._rooms[roomId] = roomInfo;
        }
    }, {
        key: 'getEndToEndRooms',
        value: function getEndToEndRooms(txn, func) {
            func(this._rooms);
        }
    }, {
        key: 'getSessionsNeedingBackup',
        value: function getSessionsNeedingBackup(limit) {
            var sessions = [];
            for (var session in this._sessionsNeedingBackup) {
                if (this._inboundGroupSessions[session]) {
                    sessions.push({
                        senderKey: session.substr(0, 43),
                        sessionId: session.substr(44),
                        sessionData: this._inboundGroupSessions[session]
                    });
                    if (limit && session.length >= limit) {
                        break;
                    }
                }
            }
            return _bluebird2.default.resolve(sessions);
        }
    }, {
        key: 'countSessionsNeedingBackup',
        value: function countSessionsNeedingBackup() {
            return _bluebird2.default.resolve((0, _keys2.default)(this._sessionsNeedingBackup).length);
        }
    }, {
        key: 'unmarkSessionsNeedingBackup',
        value: function unmarkSessionsNeedingBackup(sessions) {
            var _iteratorNormalCompletion10 = true;
            var _didIteratorError10 = false;
            var _iteratorError10 = undefined;

            try {
                for (var _iterator10 = (0, _getIterator3.default)(sessions), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
                    var session = _step10.value;

                    var sessionKey = session.senderKey + '/' + session.sessionId;
                    delete this._sessionsNeedingBackup[sessionKey];
                }
            } catch (err) {
                _didIteratorError10 = true;
                _iteratorError10 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion10 && _iterator10.return) {
                        _iterator10.return();
                    }
                } finally {
                    if (_didIteratorError10) {
                        throw _iteratorError10;
                    }
                }
            }

            return _bluebird2.default.resolve();
        }
    }, {
        key: 'markSessionsNeedingBackup',
        value: function markSessionsNeedingBackup(sessions) {
            var _iteratorNormalCompletion11 = true;
            var _didIteratorError11 = false;
            var _iteratorError11 = undefined;

            try {
                for (var _iterator11 = (0, _getIterator3.default)(sessions), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
                    var session = _step11.value;

                    var sessionKey = session.senderKey + '/' + session.sessionId;
                    this._sessionsNeedingBackup[sessionKey] = true;
                }
            } catch (err) {
                _didIteratorError11 = true;
                _iteratorError11 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion11 && _iterator11.return) {
                        _iterator11.return();
                    }
                } finally {
                    if (_didIteratorError11) {
                        throw _iteratorError11;
                    }
                }
            }

            return _bluebird2.default.resolve();
        }

        // Session key backups

    }, {
        key: 'doTxn',
        value: function doTxn(mode, stores, func) {
            return _bluebird2.default.resolve(func(null));
        }
    }]);
    return MemoryCryptoStore;
}(); /*
     Copyright 2017 Vector Creations Ltd
     Copyright 2018 New Vector 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.
     */

exports.default = MemoryCryptoStore;
//# sourceMappingURL=memory-crypto-store.js.map