1 | ;
|
2 |
|
3 | var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
|
4 |
|
5 | var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
|
6 |
|
7 | var _getIterator2 = require('babel-runtime/core-js/get-iterator');
|
8 |
|
9 | var _getIterator3 = _interopRequireDefault(_getIterator2);
|
10 |
|
11 | var _stringify = require('babel-runtime/core-js/json/stringify');
|
12 |
|
13 | var _stringify2 = _interopRequireDefault(_stringify);
|
14 |
|
15 | var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
|
16 |
|
17 | var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
|
18 |
|
19 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
|
20 |
|
21 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
|
22 |
|
23 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
|
24 |
|
25 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
|
26 |
|
27 | var _inherits2 = require('babel-runtime/helpers/inherits');
|
28 |
|
29 | var _inherits3 = _interopRequireDefault(_inherits2);
|
30 |
|
31 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
32 |
|
33 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
34 |
|
35 | var _createClass2 = require('babel-runtime/helpers/createClass');
|
36 |
|
37 | var _createClass3 = _interopRequireDefault(_createClass2);
|
38 |
|
39 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
40 |
|
41 | var ChatServiceError = require('./ChatServiceError');
|
42 | var Promise = require('bluebird');
|
43 | var Redis = require('ioredis');
|
44 | var Room = require('./Room');
|
45 | var User = require('./User');
|
46 | var _ = require('lodash');
|
47 | var promiseRetry = require('promise-retry');
|
48 | var uid = require('uid-safe');
|
49 |
|
50 | var _require = require('es6-mixin');
|
51 |
|
52 | var mixin = _require.mixin;
|
53 |
|
54 |
|
55 | var namespace = 'chatservice';
|
56 |
|
57 | function initSet(redis, set, values) {
|
58 | return redis.del(set).then(function () {
|
59 | if (!values) {
|
60 | return Promise.resolve();
|
61 | } else {
|
62 | return redis.sadd(set, values);
|
63 | }
|
64 | });
|
65 | }
|
66 |
|
67 | // State init/remove operations.
|
68 |
|
69 | var StateOperations = function () {
|
70 | function StateOperations(name, exitsErrorName, redis, makeKeyName, stateReset) {
|
71 | (0, _classCallCheck3.default)(this, StateOperations);
|
72 |
|
73 | this.name = name;
|
74 | this.exitsErrorName = exitsErrorName;
|
75 | this.redis = redis;
|
76 | this.makeKeyName = makeKeyName;
|
77 | this.stateReset = stateReset;
|
78 | }
|
79 |
|
80 | (0, _createClass3.default)(StateOperations, [{
|
81 | key: 'initState',
|
82 | value: function initState(state) {
|
83 | var _this = this;
|
84 |
|
85 | return this.redis.setnx(this.makeKeyName('exists'), true).then(function (isnew) {
|
86 | if (!isnew) {
|
87 | var error = new ChatServiceError(_this.exitsErrorName, _this.name);
|
88 | return Promise.reject(error);
|
89 | } else {
|
90 | return Promise.resolve();
|
91 | }
|
92 | }).then(function () {
|
93 | return _this.stateReset(state);
|
94 | }).then(function () {
|
95 | return _this.redis.setnx(_this.makeKeyName('isInit'), true);
|
96 | });
|
97 | }
|
98 | }, {
|
99 | key: 'removeState',
|
100 | value: function removeState() {
|
101 | var _this2 = this;
|
102 |
|
103 | return this.stateReset().then(function () {
|
104 | return _this2.redis.del(_this2.makeKeyName('exists'), _this2.makeKeyName('isInit'));
|
105 | });
|
106 | }
|
107 | }, {
|
108 | key: 'startRemoving',
|
109 | value: function startRemoving() {
|
110 | return this.redis.del(this.makeKeyName('isInit'));
|
111 | }
|
112 | }]);
|
113 | return StateOperations;
|
114 | }();
|
115 |
|
116 | // Redis lock operations.
|
117 |
|
118 |
|
119 | var LockOperations = function () {
|
120 | function LockOperations(redis) {
|
121 | (0, _classCallCheck3.default)(this, LockOperations);
|
122 |
|
123 | this.redis = redis;
|
124 | }
|
125 |
|
126 | (0, _createClass3.default)(LockOperations, [{
|
127 | key: 'lock',
|
128 | value: function lock(key, val, ttl) {
|
129 | var _this3 = this;
|
130 |
|
131 | return promiseRetry({ minTimeout: 100, retries: 10, factor: 1.5, randomize: true }, function (retry, n) {
|
132 | return _this3.redis.set(key, val, 'NX', 'PX', ttl).then(function (res) {
|
133 | if (!res) {
|
134 | var error = new ChatServiceError('timeout');
|
135 | return retry(error);
|
136 | } else {
|
137 | return null;
|
138 | }
|
139 | }).catch(retry);
|
140 | });
|
141 | }
|
142 | }, {
|
143 | key: 'unlock',
|
144 | value: function unlock(key, val) {
|
145 | return this.redis.unlock(key, val);
|
146 | }
|
147 | }]);
|
148 | return LockOperations;
|
149 | }();
|
150 |
|
151 | // Redis scripts.
|
152 |
|
153 |
|
154 | var luaCommands = {
|
155 | unlock: {
|
156 | numberOfKeys: 1,
|
157 | lua: '\nif redis.call("get",KEYS[1]) == ARGV[1] then\n return redis.call("del",KEYS[1])\nelse\n return 0\nend'
|
158 | },
|
159 |
|
160 | messageAdd: {
|
161 | numberOfKeys: 5,
|
162 | lua: '\nlocal msg = ARGV[1]\nlocal ts = ARGV[2]\n\nlocal lastMessageId = KEYS[1]\nlocal historyMaxSize = KEYS[2]\nlocal messagesIds = KEYS[3]\nlocal messagesTimestamps = KEYS[4]\nlocal messagesHistory = KEYS[5]\n\nlocal id = tonumber(redis.call(\'INCR\', lastMessageId))\nlocal maxsz = tonumber(redis.call(\'GET\', historyMaxSize))\n\nredis.call(\'LPUSH\', messagesIds, id)\nredis.call(\'LPUSH\', messagesTimestamps, ts)\nredis.call(\'LPUSH\', messagesHistory, msg)\n\nlocal sz = tonumber(redis.call(\'LLEN\', messagesHistory))\n\nif sz > maxsz then\n redis.call(\'RPOP\', messagesIds)\n redis.call(\'RPOP\', messagesTimestamps)\n redis.call(\'RPOP\', messagesHistory)\nend\n\nreturn {id}'
|
163 | },
|
164 |
|
165 | messagesGet: {
|
166 | numberOfKeys: 5,
|
167 | lua: '\nlocal id = ARGV[1]\nlocal maxlen = ARGV[2]\n\nlocal lastMessageId = KEYS[1]\nlocal historyMaxSize = KEYS[2]\nlocal messagesIds = KEYS[3]\nlocal messagesTimestamps = KEYS[4]\nlocal messagesHistory = KEYS[5]\n\nlocal lastid = tonumber(redis.call(\'GET\', lastMessageId))\nlocal maxsz = tonumber(redis.call(\'GET\', historyMaxSize))\nlocal id = math.min(id, lastid)\nlocal endp = lastid - id\nlocal len = math.min(maxlen, endp)\nlocal start = math.max(0, endp - len)\n\nif start >= endp then\n return {}\nend\n\nendp = endp - 1\nlocal msgs = redis.call(\'LRANGE\', messagesHistory, start, endp)\nlocal tss = redis.call(\'LRANGE\', messagesTimestamps, start, endp)\nlocal ids = redis.call(\'LRANGE\', messagesIds, start, endp)\n\nreturn {msgs, tss, ids}'
|
168 | },
|
169 |
|
170 | getSocketsToRooms: {
|
171 | numberOfKeys: 1,
|
172 | lua: '\nlocal result = {}\nlocal sockets = KEYS[1]\nlocal prefix = ARGV[1]\nlocal ids = redis.call(\'HKEYS\', sockets)\n\nif table.getn(ids) == 0 then\n local jsonResult = cjson.encode(cjson.null)\n return {jsonResult}\nend\n\nfor i, id in pairs(ids) do\n local joined = redis.call(\'SMEMBERS\', prefix .. id)\n result[id] = joined\nend\n\nlocal jsonResult = cjson.encode(result)\nreturn {jsonResult}'
|
173 | },
|
174 |
|
175 | removeAllSocketsFromRoom: {
|
176 | numberOfKeys: 1,
|
177 | lua: '\nlocal room = KEYS[1]\nlocal prefix = ARGV[1]\nlocal roomName = ARGV[2]\nlocal ids = redis.call(\'SMEMBERS\', room)\n\nif table.getn(ids) == 0 then\n local jsonResult = cjson.encode(cjson.null)\n return {jsonResult}\nend\n\nredis.call(\'DEL\', room)\n\nfor i, id in pairs(ids) do\n redis.call(\'SREM\', prefix .. id, roomName)\nend\n\nlocal jsonResult = cjson.encode(ids)\nreturn {jsonResult}'
|
178 | },
|
179 |
|
180 | removeSocket: {
|
181 | numberOfKeys: 2,
|
182 | lua: '\nlocal id = KEYS[1]\nlocal sockets = KEYS[2]\nlocal prefix = ARGV[1]\nlocal socketid = ARGV[2]\n\nlocal rooms = redis.call(\'SMEMBERS\', id)\nredis.call(\'DEL\', id)\n\nredis.call(\'HDEL\', sockets, socketid)\nlocal nconnected = redis.call(\'HLEN\', sockets)\n\nlocal removedRooms = {}\nlocal joinedSockets = {}\n\nfor i, room in pairs(rooms) do\n local ismember = redis.call(\'SISMEMBER\', prefix .. room, socketid)\n if ismember == 1 then\n redis.call(\'SREM\', prefix .. room, socketid)\n local njoined = redis.call(\'SCARD\', prefix .. room)\n table.insert(removedRooms, room)\n table.insert(joinedSockets, njoined)\n end\nend\n\nif table.getn(removedRooms) == 0 or table.getn(rooms) == 0 then\n local jsonResult = cjson.encode({cjson.null, cjson.null, nconnected})\n return {jsonResult}\nend\n\nlocal jsonResult = cjson.encode({removedRooms, joinedSockets, nconnected})\nreturn {jsonResult}'
|
183 | }
|
184 |
|
185 | };
|
186 |
|
187 | // Implements state API lists management.
|
188 |
|
189 | var ListsStateRedis = function () {
|
190 | function ListsStateRedis() {
|
191 | (0, _classCallCheck3.default)(this, ListsStateRedis);
|
192 | }
|
193 |
|
194 | (0, _createClass3.default)(ListsStateRedis, [{
|
195 | key: 'makeKeyName',
|
196 | value: function makeKeyName(keyName) {
|
197 | return namespace + ':' + this.prefix + ':{' + this.name + '}:' + keyName;
|
198 | }
|
199 | }, {
|
200 | key: 'checkList',
|
201 | value: function checkList(listName, num, limit) {
|
202 | if (!this.hasList(listName)) {
|
203 | var error = new ChatServiceError('noList', listName);
|
204 | return Promise.reject(error);
|
205 | }
|
206 | if (listName === 'userlist') {
|
207 | return Promise.resolve();
|
208 | }
|
209 | return this.redis.scard(listName).then(function (sz) {
|
210 | if (sz + num > limit) {
|
211 | var _error = new ChatServiceError('listLimitExceeded', listName);
|
212 | return Promise.reject(_error);
|
213 | } else {
|
214 | return Promise.resolve();
|
215 | }
|
216 | });
|
217 | }
|
218 | }, {
|
219 | key: 'addToList',
|
220 | value: function addToList(listName, elems, limit) {
|
221 | var _this4 = this;
|
222 |
|
223 | var num = elems.length;
|
224 | return this.checkList(listName, num, limit).then(function () {
|
225 | return _this4.redis.sadd(_this4.makeKeyName(listName), elems);
|
226 | });
|
227 | }
|
228 | }, {
|
229 | key: 'removeFromList',
|
230 | value: function removeFromList(listName, elems) {
|
231 | var _this5 = this;
|
232 |
|
233 | return this.checkList(listName).then(function () {
|
234 | return _this5.redis.srem(_this5.makeKeyName(listName), elems);
|
235 | });
|
236 | }
|
237 | }, {
|
238 | key: 'getList',
|
239 | value: function getList(listName) {
|
240 | var _this6 = this;
|
241 |
|
242 | return this.checkList(listName).then(function () {
|
243 | return _this6.redis.smembers(_this6.makeKeyName(listName));
|
244 | });
|
245 | }
|
246 | }, {
|
247 | key: 'hasInList',
|
248 | value: function hasInList(listName, elem) {
|
249 | var _this7 = this;
|
250 |
|
251 | return this.checkList(listName).then(function () {
|
252 | return _this7.redis.sismember(_this7.makeKeyName(listName), elem);
|
253 | }).then(function (data) {
|
254 | return Promise.resolve(Boolean(data));
|
255 | });
|
256 | }
|
257 | }, {
|
258 | key: 'whitelistOnlySet',
|
259 | value: function whitelistOnlySet(mode) {
|
260 | var whitelistOnly = mode ? true : '';
|
261 | return this.redis.set(this.makeKeyName('whitelistMode'), whitelistOnly);
|
262 | }
|
263 | }, {
|
264 | key: 'whitelistOnlyGet',
|
265 | value: function whitelistOnlyGet() {
|
266 | return this.redis.get(this.makeKeyName('whitelistMode')).then(function (data) {
|
267 | return Promise.resolve(Boolean(data));
|
268 | });
|
269 | }
|
270 | }]);
|
271 | return ListsStateRedis;
|
272 | }();
|
273 |
|
274 | // Implements room state API.
|
275 |
|
276 |
|
277 | var RoomStateRedis = function (_ListsStateRedis) {
|
278 | (0, _inherits3.default)(RoomStateRedis, _ListsStateRedis);
|
279 |
|
280 | function RoomStateRedis(server, roomName) {
|
281 | (0, _classCallCheck3.default)(this, RoomStateRedis);
|
282 |
|
283 | var _this8 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(RoomStateRedis).call(this));
|
284 |
|
285 | _this8.server = server;
|
286 | _this8.roomName = roomName;
|
287 | _this8.name = _this8.roomName;
|
288 | _this8.historyMaxGetMessages = _this8.server.historyMaxGetMessages;
|
289 | _this8.redis = _this8.server.redis;
|
290 | _this8.exitsErrorName = 'roomExists';
|
291 | _this8.prefix = 'rooms';
|
292 | mixin(_this8, StateOperations, _this8.name, _this8.exitsErrorName, _this8.redis, _this8.makeKeyName.bind(_this8), _this8.stateReset.bind(_this8));
|
293 | return _this8;
|
294 | }
|
295 |
|
296 | (0, _createClass3.default)(RoomStateRedis, [{
|
297 | key: 'stateReset',
|
298 | value: function stateReset(state) {
|
299 | state = state || {};
|
300 | var _state = state;
|
301 | var whitelist = _state.whitelist;
|
302 | var blacklist = _state.blacklist;
|
303 | var adminlist = _state.adminlist;
|
304 | var whitelistOnly = _state.whitelistOnly;
|
305 | var owner = _state.owner;
|
306 | var historyMaxSize = _state.historyMaxSize;
|
307 | var _state$enableAccessLi = _state.enableAccessListsUpdates;
|
308 | var enableAccessListsUpdates = _state$enableAccessLi === undefined ? this.server.enableAccessListsUpdates : _state$enableAccessLi;
|
309 | var _state$enableUserlist = _state.enableUserlistUpdates;
|
310 | var enableUserlistUpdates = _state$enableUserlist === undefined ? this.server.enableUserlistUpdates : _state$enableUserlist;
|
311 |
|
312 | if (!owner) {
|
313 | owner = '';
|
314 | }
|
315 | return Promise.all([initSet(this.redis, this.makeKeyName('whitelist'), whitelist), initSet(this.redis, this.makeKeyName('blacklist'), blacklist), initSet(this.redis, this.makeKeyName('adminlist'), adminlist), initSet(this.redis, this.makeKeyName('userlist'), null), this.redis.del(this.makeKeyName('messagesHistory')), this.redis.del(this.makeKeyName('messagesTimestamps')), this.redis.del(this.makeKeyName('messagesIds')), this.redis.del(this.makeKeyName('usersseen')), this.redis.set(this.makeKeyName('lastMessageId'), 0), this.redis.set(this.makeKeyName('owner'), owner), this.whitelistOnlySet(whitelistOnly), this.accessListsUpdatesSet(enableAccessListsUpdates), this.userlistUpdatesSet(enableUserlistUpdates), this.historyMaxSizeSet(historyMaxSize)]).return();
|
316 | }
|
317 | }, {
|
318 | key: 'hasList',
|
319 | value: function hasList(listName) {
|
320 | return listName === 'adminlist' || listName === 'whitelist' || listName === 'blacklist' || listName === 'userlist';
|
321 | }
|
322 | }, {
|
323 | key: 'ownerGet',
|
324 | value: function ownerGet() {
|
325 | return this.redis.get(this.makeKeyName('owner'));
|
326 | }
|
327 | }, {
|
328 | key: 'ownerSet',
|
329 | value: function ownerSet(owner) {
|
330 | return this.redis.set(this.makeKeyName('owner'), owner);
|
331 | }
|
332 | }, {
|
333 | key: 'accessListsUpdatesSet',
|
334 | value: function accessListsUpdatesSet(enableAccessListsUpdates) {
|
335 | enableAccessListsUpdates = enableAccessListsUpdates ? true : '';
|
336 | return this.redis.set(this.makeKeyName('enableAccessListsUpdates'), enableAccessListsUpdates);
|
337 | }
|
338 | }, {
|
339 | key: 'accessListsUpdatesGet',
|
340 | value: function accessListsUpdatesGet() {
|
341 | return this.redis.get(this.makeKeyName('enableAccessListsUpdates')).then(function (data) {
|
342 | return Promise.resolve(Boolean(data));
|
343 | });
|
344 | }
|
345 | }, {
|
346 | key: 'userlistUpdatesSet',
|
347 | value: function userlistUpdatesSet(enableUserlistUpdates) {
|
348 | enableUserlistUpdates = enableUserlistUpdates ? true : '';
|
349 | return this.redis.set(this.makeKeyName('enableUserlistUpdates'), enableUserlistUpdates);
|
350 | }
|
351 | }, {
|
352 | key: 'userlistUpdatesGet',
|
353 | value: function userlistUpdatesGet() {
|
354 | return this.redis.get(this.makeKeyName('enableUserlistUpdates')).then(function (data) {
|
355 | return Promise.resolve(Boolean(data));
|
356 | });
|
357 | }
|
358 | }, {
|
359 | key: 'historyMaxSizeSet',
|
360 | value: function historyMaxSizeSet(historyMaxSize) {
|
361 | var limit = historyMaxSize;
|
362 | if (!(_.isNumber(historyMaxSize) && historyMaxSize >= 0)) {
|
363 | limit = this.server.historyMaxSize;
|
364 | }
|
365 | if (limit === 0) {
|
366 | return this.redis.multi().set(this.makeKeyName('historyMaxSize'), limit).del(this.makeKeyName('messagesHistory')).del(this.makeKeyName('messagesTimestamps')).del(this.makeKeyName('messagesIds')).exec();
|
367 | } else {
|
368 | var last = limit - 1;
|
369 | return this.redis.multi().set(this.makeKeyName('historyMaxSize'), limit).ltrim(this.makeKeyName('messagesHistory'), 0, last).ltrim(this.makeKeyName('messagesTimestamps'), 0, last).ltrim(this.makeKeyName('messagesIds'), 0, last).exec();
|
370 | }
|
371 | }
|
372 | }, {
|
373 | key: 'historyInfo',
|
374 | value: function historyInfo() {
|
375 | var _this9 = this;
|
376 |
|
377 | return this.redis.multi().get(this.makeKeyName('historyMaxSize')).llen(this.makeKeyName('messagesHistory')).get(this.makeKeyName('lastMessageId')).exec().spread(function (_ref, _ref2, _ref3) {
|
378 | var _ref6 = (0, _slicedToArray3.default)(_ref, 2);
|
379 |
|
380 | var historyMaxSize = _ref6[1];
|
381 |
|
382 | var _ref5 = (0, _slicedToArray3.default)(_ref2, 2);
|
383 |
|
384 | var historySize = _ref5[1];
|
385 |
|
386 | var _ref4 = (0, _slicedToArray3.default)(_ref3, 2);
|
387 |
|
388 | var lastMessageId = _ref4[1];
|
389 |
|
390 | historySize = parseInt(historySize);
|
391 | historyMaxSize = parseFloat(historyMaxSize);
|
392 | lastMessageId = parseInt(lastMessageId);
|
393 | var info = { historySize: historySize,
|
394 | historyMaxSize: historyMaxSize,
|
395 | historyMaxGetMessages: _this9.historyMaxGetMessages,
|
396 | lastMessageId: lastMessageId };
|
397 | return Promise.resolve(info);
|
398 | });
|
399 | }
|
400 | }, {
|
401 | key: 'getCommonUsers',
|
402 | value: function getCommonUsers() {
|
403 | return this.redis.sdiff(this.makeKeyName('userlist'), this.makeKeyName('whitelist'), this.makeKeyName('adminlist'));
|
404 | }
|
405 | }, {
|
406 | key: 'messageAdd',
|
407 | value: function messageAdd(msg) {
|
408 | var timestamp = _.now();
|
409 | var smsg = (0, _stringify2.default)(msg);
|
410 | return this.redis.messageAdd(this.makeKeyName('lastMessageId'), this.makeKeyName('historyMaxSize'), this.makeKeyName('messagesIds'), this.makeKeyName('messagesTimestamps'), this.makeKeyName('messagesHistory'), smsg, timestamp).spread(function (id) {
|
411 | msg.id = id;
|
412 | msg.timestamp = timestamp;
|
413 | return Promise.resolve(msg);
|
414 | });
|
415 | }
|
416 | }, {
|
417 | key: 'convertMessages',
|
418 | value: function convertMessages(msgs, tss, ids) {
|
419 | var data = [];
|
420 | if (!msgs) {
|
421 | return Promise.resolve(data);
|
422 | }
|
423 | for (var idx = 0; idx < msgs.length; idx++) {
|
424 | var msg = msgs[idx];
|
425 | var obj = JSON.parse(msg, function (key, val) {
|
426 | if (val && val.type === 'Buffer') {
|
427 | return new Buffer(val.data);
|
428 | } else {
|
429 | return val;
|
430 | }
|
431 | });
|
432 | obj.timestamp = parseInt(tss[idx]);
|
433 | obj.id = parseInt(ids[idx]);
|
434 | data[idx] = obj;
|
435 | }
|
436 | return Promise.resolve(data);
|
437 | }
|
438 | }, {
|
439 | key: 'messagesGetRecent',
|
440 | value: function messagesGetRecent() {
|
441 | var _this10 = this;
|
442 |
|
443 | if (this.historyMaxGetMessages <= 0) {
|
444 | return Promise.resolve([]);
|
445 | }
|
446 | var limit = this.historyMaxGetMessages - 1;
|
447 | return this.redis.multi().lrange(this.makeKeyName('messagesHistory'), 0, limit).lrange(this.makeKeyName('messagesTimestamps'), 0, limit).lrange(this.makeKeyName('messagesIds'), 0, limit).exec().spread(function (_ref7, _ref8, _ref9) {
|
448 | var _ref12 = (0, _slicedToArray3.default)(_ref7, 2);
|
449 |
|
450 | var msgs = _ref12[1];
|
451 |
|
452 | var _ref11 = (0, _slicedToArray3.default)(_ref8, 2);
|
453 |
|
454 | var tss = _ref11[1];
|
455 |
|
456 | var _ref10 = (0, _slicedToArray3.default)(_ref9, 2);
|
457 |
|
458 | var ids = _ref10[1];
|
459 |
|
460 | return _this10.convertMessages(msgs, tss, ids);
|
461 | });
|
462 | }
|
463 | }, {
|
464 | key: 'messagesGet',
|
465 | value: function messagesGet(id) {
|
466 | var _this11 = this;
|
467 |
|
468 | var maxMessages = arguments.length <= 1 || arguments[1] === undefined ? this.historyMaxGetMessages : arguments[1];
|
469 |
|
470 | if (maxMessages <= 0) {
|
471 | return Promise.resolve([]);
|
472 | }
|
473 | id = _.max([0, id]);
|
474 | return this.redis.messagesGet(this.makeKeyName('lastMessageId'), this.makeKeyName('historyMaxSize'), this.makeKeyName('messagesIds'), this.makeKeyName('messagesTimestamps'), this.makeKeyName('messagesHistory'), id, maxMessages).spread(function (msgs, tss, ids) {
|
475 | return _this11.convertMessages(msgs, tss, ids);
|
476 | });
|
477 | }
|
478 | }, {
|
479 | key: 'userSeenGet',
|
480 | value: function userSeenGet(userName) {
|
481 | return this.redis.multi().hget(this.makeKeyName('usersseen'), userName).sismember(this.makeKeyName('userlist'), userName).exec().spread(function (_ref13, _ref14) {
|
482 | var _ref16 = (0, _slicedToArray3.default)(_ref13, 2);
|
483 |
|
484 | var ts = _ref16[1];
|
485 |
|
486 | var _ref15 = (0, _slicedToArray3.default)(_ref14, 2);
|
487 |
|
488 | var isjoined = _ref15[1];
|
489 |
|
490 | var joined = Boolean(isjoined);
|
491 | var timestamp = ts ? parseInt(ts) : null;
|
492 | return { joined: joined, timestamp: timestamp };
|
493 | });
|
494 | }
|
495 | }, {
|
496 | key: 'userSeenUpdate',
|
497 | value: function userSeenUpdate(userName) {
|
498 | var timestamp = _.now();
|
499 | return this.redis.hset(this.makeKeyName('usersseen'), userName, timestamp);
|
500 | }
|
501 | }]);
|
502 | return RoomStateRedis;
|
503 | }(ListsStateRedis);
|
504 |
|
505 | // Implements direct messaging state API.
|
506 |
|
507 |
|
508 | var DirectMessagingStateRedis = function (_ListsStateRedis2) {
|
509 | (0, _inherits3.default)(DirectMessagingStateRedis, _ListsStateRedis2);
|
510 |
|
511 | function DirectMessagingStateRedis(server, userName) {
|
512 | (0, _classCallCheck3.default)(this, DirectMessagingStateRedis);
|
513 |
|
514 | var _this12 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(DirectMessagingStateRedis).call(this));
|
515 |
|
516 | _this12.server = server;
|
517 | _this12.userName = userName;
|
518 | _this12.name = _this12.userName;
|
519 | _this12.prefix = 'users';
|
520 | _this12.exitsErrorName = 'userExists';
|
521 | _this12.redis = _this12.server.redis;
|
522 | mixin(_this12, StateOperations, _this12.name, _this12.exitsErrorName, _this12.redis, _this12.makeKeyName.bind(_this12), _this12.stateReset.bind(_this12));
|
523 | return _this12;
|
524 | }
|
525 |
|
526 | (0, _createClass3.default)(DirectMessagingStateRedis, [{
|
527 | key: 'hasList',
|
528 | value: function hasList(listName) {
|
529 | return listName === 'whitelist' || listName === 'blacklist';
|
530 | }
|
531 | }, {
|
532 | key: 'stateReset',
|
533 | value: function stateReset(state) {
|
534 | state = state || {};
|
535 | var _state2 = state;
|
536 | var whitelist = _state2.whitelist;
|
537 | var blacklist = _state2.blacklist;
|
538 | var whitelistOnly = _state2.whitelistOnly;
|
539 |
|
540 | whitelistOnly = whitelistOnly ? true : '';
|
541 | return Promise.all([initSet(this.redis, this.makeKeyName('whitelist'), whitelist), initSet(this.redis, this.makeKeyName('blacklist'), blacklist), this.redis.set(this.makeKeyName('whitelistMode'), whitelistOnly)]).return();
|
542 | }
|
543 | }]);
|
544 | return DirectMessagingStateRedis;
|
545 | }(ListsStateRedis);
|
546 |
|
547 | // Implements user state API.
|
548 |
|
549 |
|
550 | var UserStateRedis = function () {
|
551 | function UserStateRedis(server, userName) {
|
552 | (0, _classCallCheck3.default)(this, UserStateRedis);
|
553 |
|
554 | this.server = server;
|
555 | this.userName = userName;
|
556 | this.name = this.userName;
|
557 | this.prefix = 'users';
|
558 | this.redis = this.server.redis;
|
559 | mixin(this, LockOperations, this.redis);
|
560 | }
|
561 |
|
562 | (0, _createClass3.default)(UserStateRedis, [{
|
563 | key: 'makeKeyName',
|
564 | value: function makeKeyName(keyName) {
|
565 | return namespace + ':' + this.prefix + ':{' + this.name + '}:' + keyName;
|
566 | }
|
567 | }, {
|
568 | key: 'makeSocketToRooms',
|
569 | value: function makeSocketToRooms() {
|
570 | var id = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
|
571 |
|
572 | return this.makeKeyName('socketsToRooms:' + id);
|
573 | }
|
574 | }, {
|
575 | key: 'makeRoomToSockets',
|
576 | value: function makeRoomToSockets() {
|
577 | var room = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
|
578 |
|
579 | return this.makeKeyName('roomsToSockets:' + room);
|
580 | }
|
581 | }, {
|
582 | key: 'makeRoomLock',
|
583 | value: function makeRoomLock(room) {
|
584 | return this.makeKeyName('roomLock:' + room);
|
585 | }
|
586 | }, {
|
587 | key: 'addSocket',
|
588 | value: function addSocket(id, uid) {
|
589 | return this.redis.multi().hset(this.makeKeyName('sockets'), id, uid).hlen(this.makeKeyName('sockets')).exec().spread(function (_, _ref17) {
|
590 | var _ref18 = (0, _slicedToArray3.default)(_ref17, 2);
|
591 |
|
592 | var nconnected = _ref18[1];
|
593 | return Promise.resolve(nconnected);
|
594 | });
|
595 | }
|
596 | }, {
|
597 | key: 'getAllSockets',
|
598 | value: function getAllSockets() {
|
599 | return this.redis.hkeys(this.makeKeyName('sockets'));
|
600 | }
|
601 | }, {
|
602 | key: 'getSocketsToInstance',
|
603 | value: function getSocketsToInstance() {
|
604 | return this.redis.hgetall(this.makeKeyName('sockets'));
|
605 | }
|
606 | }, {
|
607 | key: 'getRoomToSockets',
|
608 | value: function getRoomToSockets(roomName) {
|
609 | return this.redis.smembers(this.makeRoomToSockets(roomName));
|
610 | }
|
611 | }, {
|
612 | key: 'getSocketsToRooms',
|
613 | value: function getSocketsToRooms() {
|
614 | return this.redis.getSocketsToRooms(this.makeKeyName('sockets'), this.makeSocketToRooms()).spread(function (result) {
|
615 | var data = JSON.parse(result) || {};
|
616 | var _iteratorNormalCompletion = true;
|
617 | var _didIteratorError = false;
|
618 | var _iteratorError = undefined;
|
619 |
|
620 | try {
|
621 | for (var _iterator = (0, _getIterator3.default)(_.toPairs(data)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
622 | var _step$value = (0, _slicedToArray3.default)(_step.value, 2);
|
623 |
|
624 | var k = _step$value[0];
|
625 | var v = _step$value[1];
|
626 |
|
627 | if (_.isEmpty(v)) {
|
628 | data[k] = [];
|
629 | }
|
630 | }
|
631 | } catch (err) {
|
632 | _didIteratorError = true;
|
633 | _iteratorError = err;
|
634 | } finally {
|
635 | try {
|
636 | if (!_iteratorNormalCompletion && _iterator.return) {
|
637 | _iterator.return();
|
638 | }
|
639 | } finally {
|
640 | if (_didIteratorError) {
|
641 | throw _iteratorError;
|
642 | }
|
643 | }
|
644 | }
|
645 |
|
646 | return Promise.resolve(data);
|
647 | });
|
648 | }
|
649 | }, {
|
650 | key: 'addSocketToRoom',
|
651 | value: function addSocketToRoom(id, roomName) {
|
652 | return this.redis.multi().sadd(this.makeSocketToRooms(id), roomName).sadd(this.makeRoomToSockets(roomName), id).scard(this.makeRoomToSockets(roomName)).exec().then(function (_ref19) {
|
653 | var _ref20 = (0, _slicedToArray3.default)(_ref19, 3);
|
654 |
|
655 | var _ref20$ = (0, _slicedToArray3.default)(_ref20[2], 2);
|
656 |
|
657 | var njoined = _ref20$[1];
|
658 | return Promise.resolve(njoined);
|
659 | });
|
660 | }
|
661 | }, {
|
662 | key: 'removeSocketFromRoom',
|
663 | value: function removeSocketFromRoom(id, roomName) {
|
664 | return this.redis.multi().scard(this.makeRoomToSockets(roomName)).srem(this.makeSocketToRooms(id), roomName).srem(this.makeRoomToSockets(roomName), id).scard(this.makeRoomToSockets(roomName)).exec().then(function (_ref21) {
|
665 | var _ref22 = (0, _slicedToArray3.default)(_ref21, 4);
|
666 |
|
667 | var _ref22$ = (0, _slicedToArray3.default)(_ref22[0], 2);
|
668 |
|
669 | var wasjoined = _ref22$[1];
|
670 |
|
671 | var _ref22$2 = (0, _slicedToArray3.default)(_ref22[3], 2);
|
672 |
|
673 | var njoined = _ref22$2[1];
|
674 |
|
675 | var hasChanged = njoined !== wasjoined;
|
676 | return Promise.resolve([njoined, hasChanged]);
|
677 | });
|
678 | }
|
679 | }, {
|
680 | key: 'removeAllSocketsFromRoom',
|
681 | value: function removeAllSocketsFromRoom(roomName) {
|
682 | return this.redis.removeAllSocketsFromRoom(this.makeRoomToSockets(roomName), this.makeSocketToRooms(), roomName).spread(function (result) {
|
683 | return Promise.resolve(JSON.parse(result));
|
684 | });
|
685 | }
|
686 | }, {
|
687 | key: 'removeSocket',
|
688 | value: function removeSocket(id) {
|
689 | return this.redis.removeSocket(this.makeSocketToRooms(id), this.makeKeyName('sockets'), this.makeRoomToSockets(), id).spread(function (result) {
|
690 | return Promise.resolve(JSON.parse(result));
|
691 | });
|
692 | }
|
693 | }, {
|
694 | key: 'lockToRoom',
|
695 | value: function lockToRoom(roomName, ttl) {
|
696 | var _this13 = this;
|
697 |
|
698 | return uid(18).then(function (val) {
|
699 | var start = _.now();
|
700 | return _this13.lock(_this13.makeRoomLock(roomName), val, ttl).then(function () {
|
701 | return Promise.resolve().disposer(function () {
|
702 | if (start + ttl < _.now()) {
|
703 | _this13.server.emit('lockTimeExceeded', val, { userName: _this13.userName, roomName: roomName });
|
704 | }
|
705 | return _this13.unlock(_this13.makeRoomLock(roomName), val);
|
706 | });
|
707 | });
|
708 | });
|
709 | }
|
710 | }]);
|
711 | return UserStateRedis;
|
712 | }();
|
713 |
|
714 | // Implements global state API.
|
715 |
|
716 |
|
717 | var RedisState = function () {
|
718 | function RedisState(server, options) {
|
719 | (0, _classCallCheck3.default)(this, RedisState);
|
720 |
|
721 | this.server = server;
|
722 | this.options = options;
|
723 | this.closed = false;
|
724 | if (this.options.useCluster) {
|
725 | this.redis = new (Function.prototype.bind.apply(Redis.Cluster, [null].concat((0, _toConsumableArray3.default)(this.options.redisOptions))))();
|
726 | } else {
|
727 | var redisOptions = _.castArray(this.options.redisOptions);
|
728 | this.redis = new (Function.prototype.bind.apply(Redis, [null].concat((0, _toConsumableArray3.default)(redisOptions))))();
|
729 | }
|
730 | this.RoomState = RoomStateRedis;
|
731 | this.UserState = UserStateRedis;
|
732 | this.DirectMessagingState = DirectMessagingStateRedis;
|
733 | this.lockTTL = this.options.lockTTL || 10000;
|
734 | this.instanceUID = this.server.instanceUID;
|
735 | this.server.redis = this.redis;
|
736 | var _iteratorNormalCompletion2 = true;
|
737 | var _didIteratorError2 = false;
|
738 | var _iteratorError2 = undefined;
|
739 |
|
740 | try {
|
741 | for (var _iterator2 = (0, _getIterator3.default)(_.toPairs(luaCommands)), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
742 | var _step2$value = (0, _slicedToArray3.default)(_step2.value, 2);
|
743 |
|
744 | var cmd = _step2$value[0];
|
745 | var def = _step2$value[1];
|
746 |
|
747 | this.redis.defineCommand(cmd, {
|
748 | numberOfKeys: def.numberOfKeys,
|
749 | lua: def.lua
|
750 | });
|
751 | }
|
752 | } catch (err) {
|
753 | _didIteratorError2 = true;
|
754 | _iteratorError2 = err;
|
755 | } finally {
|
756 | try {
|
757 | if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
758 | _iterator2.return();
|
759 | }
|
760 | } finally {
|
761 | if (_didIteratorError2) {
|
762 | throw _iteratorError2;
|
763 | }
|
764 | }
|
765 | }
|
766 | }
|
767 |
|
768 | (0, _createClass3.default)(RedisState, [{
|
769 | key: 'makeKeyName',
|
770 | value: function makeKeyName(prefix, name, keyName) {
|
771 | return namespace + ':' + prefix + ':{' + name + '}:' + keyName;
|
772 | }
|
773 | }, {
|
774 | key: 'hasRoom',
|
775 | value: function hasRoom(name) {
|
776 | return this.redis.get(this.makeKeyName('rooms', name, 'isInit'));
|
777 | }
|
778 | }, {
|
779 | key: 'hasUser',
|
780 | value: function hasUser(name) {
|
781 | return this.redis.get(this.makeKeyName('users', name, 'isInit'));
|
782 | }
|
783 | }, {
|
784 | key: 'close',
|
785 | value: function close() {
|
786 | this.closed = true;
|
787 | return this.redis.quit().return();
|
788 | }
|
789 | }, {
|
790 | key: 'getRoom',
|
791 | value: function getRoom(name) {
|
792 | var isPredicate = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
793 |
|
794 | var room = new Room(this.server, name);
|
795 | return this.hasRoom(name).then(function (exists) {
|
796 | if (!exists) {
|
797 | if (isPredicate) {
|
798 | return Promise.resolve(null);
|
799 | } else {
|
800 | var error = new ChatServiceError('noRoom', name);
|
801 | return Promise.reject(error);
|
802 | }
|
803 | }
|
804 | return Promise.resolve(room);
|
805 | });
|
806 | }
|
807 | }, {
|
808 | key: 'addRoom',
|
809 | value: function addRoom(name, state) {
|
810 | var room = new Room(this.server, name);
|
811 | return room.initState(state).return(room);
|
812 | }
|
813 | }, {
|
814 | key: 'removeRoom',
|
815 | value: function removeRoom(name) {
|
816 | return Promise.resolve();
|
817 | }
|
818 | }, {
|
819 | key: 'addSocket',
|
820 | value: function addSocket(id, userName) {
|
821 | return this.redis.hset(this.makeKeyName('instances', this.instanceUID, 'sockets'), id, userName);
|
822 | }
|
823 | }, {
|
824 | key: 'removeSocket',
|
825 | value: function removeSocket(id) {
|
826 | return this.redis.hdel(this.makeKeyName('instances', this.instanceUID, 'sockets'), id);
|
827 | }
|
828 | }, {
|
829 | key: 'getInstanceSockets',
|
830 | value: function getInstanceSockets() {
|
831 | var uid = arguments.length <= 0 || arguments[0] === undefined ? this.instanceUID : arguments[0];
|
832 |
|
833 | return this.redis.hgetall(this.makeKeyName('instances', uid, 'sockets'));
|
834 | }
|
835 | }, {
|
836 | key: 'updateHeartbeat',
|
837 | value: function updateHeartbeat() {
|
838 | return this.redis.set(this.makeKeyName('instances', this.instanceUID, 'heartbeat'), _.now()).catchReturn();
|
839 | }
|
840 | }, {
|
841 | key: 'getInstanceHeartbeat',
|
842 | value: function getInstanceHeartbeat() {
|
843 | var uid = arguments.length <= 0 || arguments[0] === undefined ? this.instanceUID : arguments[0];
|
844 |
|
845 | return this.redis.get(this.makeKeyName('instances', uid, 'heartbeat')).then(function (ts) {
|
846 | return ts ? parseInt(ts) : null;
|
847 | });
|
848 | }
|
849 | }, {
|
850 | key: 'getOrAddUser',
|
851 | value: function getOrAddUser(name, state) {
|
852 | var user = new User(this.server, name);
|
853 | return this.hasUser(name).then(function (exists) {
|
854 | if (!exists) {
|
855 | return user.initState(state);
|
856 | } else {
|
857 | return Promise.resolve();
|
858 | }
|
859 | }).catch(ChatServiceError, function (e) {
|
860 | return user;
|
861 | }).return(user);
|
862 | }
|
863 | }, {
|
864 | key: 'getUser',
|
865 | value: function getUser(name) {
|
866 | var isPredicate = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
867 |
|
868 | var user = new User(this.server, name);
|
869 | return this.hasUser(name).then(function (exists) {
|
870 | if (!exists) {
|
871 | if (isPredicate) {
|
872 | return Promise.resolve(null);
|
873 | } else {
|
874 | var error = new ChatServiceError('noUser', name);
|
875 | return Promise.reject(error);
|
876 | }
|
877 | }
|
878 | return Promise.resolve(user);
|
879 | });
|
880 | }
|
881 | }, {
|
882 | key: 'addUser',
|
883 | value: function addUser(name, state) {
|
884 | var user = new User(this.server, name);
|
885 | return user.initState(state).return(user);
|
886 | }
|
887 | }, {
|
888 | key: 'removeUser',
|
889 | value: function removeUser(name) {
|
890 | return Promise.resolve();
|
891 | }
|
892 | }]);
|
893 | return RedisState;
|
894 | }();
|
895 |
|
896 | module.exports = RedisState;
|
897 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9SZWRpc1N0YXRlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUEsSUFBTSxtQkFBbUIsUUFBUSxvQkFBUixDQUF6QjtBQUNBLElBQU0sVUFBVSxRQUFRLFVBQVIsQ0FBaEI7QUFDQSxJQUFNLFFBQVEsUUFBUSxTQUFSLENBQWQ7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLElBQUksUUFBUSxRQUFSLENBQVY7QUFDQSxJQUFNLGVBQWUsUUFBUSxlQUFSLENBQXJCO0FBQ0EsSUFBTSxNQUFNLFFBQVEsVUFBUixDQUFaOztlQUNrQixRQUFRLFdBQVIsQzs7SUFBVixLLFlBQUEsSzs7O0FBRVIsSUFBSSxZQUFZLGFBQWhCOztBQUVBLFNBQVMsT0FBVCxDQUFrQixLQUFsQixFQUF5QixHQUF6QixFQUE4QixNQUE5QixFQUFzQztBQUNwQyxTQUFPLE1BQU0sR0FBTixDQUFVLEdBQVYsRUFBZSxJQUFmLENBQW9CLFlBQU07QUFDL0IsUUFBSSxDQUFDLE1BQUwsRUFBYTtBQUNYLGFBQU8sUUFBUSxPQUFSLEVBQVA7QUFDRCxLQUZELE1BRU87QUFDTCxhQUFPLE1BQU0sSUFBTixDQUFXLEdBQVgsRUFBZ0IsTUFBaEIsQ0FBUDtBQUNEO0FBQ0YsR0FOTSxDQUFQO0FBT0Q7O0FBR0Q7O0lBQ00sZTtBQUVKLDJCQUFhLElBQWIsRUFBbUIsY0FBbkIsRUFBbUMsS0FBbkMsRUFBMEMsV0FBMUMsRUFBdUQsVUFBdkQsRUFBbUU7QUFBQTs7QUFDakUsU0FBSyxJQUFMLEdBQVksSUFBWjtBQUNBLFNBQUssY0FBTCxHQUFzQixjQUF0QjtBQUNBLFNBQUssS0FBTCxHQUFhLEtBQWI7QUFDQSxTQUFLLFdBQUwsR0FBbUIsV0FBbkI7QUFDQSxTQUFLLFVBQUwsR0FBa0IsVUFBbEI7QUFDRDs7Ozs4QkFF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O0FBQUEsVUFBckIsV0FBcUIseURBQVAsS0FBTzs7QUFDbEMsVUFBSSxPQUFPLElBQUksSUFBSixDQUFTLEtBQUssTUFBZCxFQUFzQixJQUF0QixDQUFYO0FBQ0EsYUFBTyxLQUFLLE9BQUwsQ0FBYSxJQUFiLEVBQW1CLElBQW5CLENBQXdCLGtCQUFVO0FBQ3ZDLFlBQUksQ0FBQyxNQUFMLEVBQWE7QUFDWCxjQUFJLFdBQUosRUFBaUI7QUFDZixtQkFBTyxRQUFRLE9BQVIsQ0FBZ0IsSUFBaEIsQ0FBUDtBQUNELFdBRkQsTUFFTztBQUNMLGdCQUFJLFFBQVEsSUFBSSxnQkFBSixDQUFxQixRQUFyQixFQUErQixJQUEvQixDQUFaO0FBQ0EsbUJBQU8sUUFBUSxNQUFSLENBQWUsS0FBZixDQUFQO0FBQ0Q7QUFDRjtBQUNELGVBQU8sUUFBUSxPQUFSLENBQWdCLElBQWhCLENBQVA7QUFDRCxPQVZNLENBQVA7QUFXRDs7OzRCQUVRLEksRUFBTSxLLEVBQU87QUFDcEIsVUFBSSxPQUFPLElBQUksSUFBSixDQUFTLEtBQUssTUFBZCxFQUFzQixJQUF0QixDQUFYO0FBQ0EsYUFBTyxLQUFLLFNBQUwsQ0FBZSxLQUFmLEVBQXNCLE1BQXRCLENBQTZCLElBQTdCLENBQVA7QUFDRDs7OytCQUVXLEksRUFBTTtBQUNoQixhQUFPLFFBQVEsT0FBUixFQUFQO0FBQ0Q7Ozs7O0FBSUgsT0FBTyxPQUFQLEdBQWlCLFVBQWpCIiwiZmlsZSI6IlJlZGlzU3RhdGUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCdcblxuY29uc3QgQ2hhdFNlcnZpY2VFcnJvciA9IHJlcXVpcmUoJy4vQ2hhdFNlcnZpY2VFcnJvcicpXG5jb25zdCBQcm9taXNlID0gcmVxdWlyZSgnYmx1ZWJpcmQnKVxuY29uc3QgUmVkaXMgPSByZXF1aXJlKCdpb3JlZGlzJylcbmNvbnN0IFJvb20gPSByZXF1aXJlKCcuL1Jvb20nKVxuY29uc3QgVXNlciA9IHJlcXVpcmUoJy4vVXNlcicpXG5jb25zdCBfID0gcmVxdWlyZSgnbG9kYXNoJylcbmNvbnN0IHByb21pc2VSZXRyeSA9IHJlcXVpcmUoJ3Byb21pc2UtcmV0cnknKVxuY29uc3QgdWlkID0gcmVxdWlyZSgndWlkLXNhZmUnKVxuY29uc3QgeyBtaXhpbiB9ID0gcmVxdWlyZSgnZXM2LW1peGluJylcblxubGV0IG5hbWVzcGFjZSA9ICdjaGF0c2VydmljZSdcblxuZnVuY3Rpb24gaW5pdFNldCAocmVkaXMsIHNldCwgdmFsdWVzKSB7XG4gIHJldHVybiByZWRpcy5kZWwoc2V0KS50aGVuKCgpID0+IHtcbiAgICBpZiAoIXZhbHVlcykge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiByZWRpcy5zYWRkKHNldCwgdmFsdWVzKVxuICAgIH1cbiAgfSlcbn1cblxuXG4vLyBTdGF0ZSBpbml0L3JlbW92ZSBvcGVyYXRpb25zLlxuY2xhc3MgU3RhdGVPcGVyYXRpb25zIHtcblxuICBjb25zdHJ1Y3RvciAobmFtZSwgZXhpdHNFcnJvck5hbWUsIHJlZGlzLCBtYWtlS2V5TmFtZSwgc3RhdGVSZXNldCkge1xuICAgIHRoaXMubmFtZSA9IG5hbWVcbiAgICB0aGlzLmV4aXRzRXJyb3JOYW1lID0gZXhpdHNFcnJvck5hbWVcbiAgICB0aGlzLnJlZGlzID0gcmVkaXNcbiAgICB0aGlzLm1ha2VLZXlOYW1lID0gbWFrZUtleU5hbWVcbiAgICB0aGlzLnN0YXRlUmVzZXQgPSBzdGF0ZVJlc2V0XG4gIH1cblxuICBpbml0U3RhdGUgKHN0YXRlKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0bngodGhpcy5tYWtlS2V5TmFtZSgnZXhpc3RzJyksIHRydWUpLnRoZW4oaXNuZXcgPT4ge1xuICAgICAgaWYgKCFpc25ldykge1xuICAgICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcih0aGlzLmV4aXRzRXJyb3JOYW1lLCB0aGlzLm5hbWUpXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnJvcilcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKVxuICAgICAgfVxuICAgIH0pLnRoZW4oKCkgPT4gdGhpcy5zdGF0ZVJlc2V0KHN0YXRlKSlcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc2V0bngodGhpcy5tYWtlS2V5TmFtZSgnaXNJbml0JyksIHRydWUpKVxuICB9XG5cbiAgcmVtb3ZlU3RhdGUgKCkge1xuICAgIHJldHVybiB0aGlzLnN0YXRlUmVzZXQoKS50aGVuKCgpID0+IHtcbiAgICAgIHJldHVybiB0aGlzLnJlZGlzLmRlbChcbiAgICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnZXhpc3RzJyksIHRoaXMubWFrZUtleU5hbWUoJ2lzSW5pdCcpKVxuICAgIH0pXG4gIH1cblxuICBzdGFydFJlbW92aW5nICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnaXNJbml0JykpXG4gIH1cblxufVxuXG4vLyBSZWRpcyBsb2NrIG9wZXJhdGlvbnMuXG5jbGFzcyBMb2NrT3BlcmF0aW9ucyB7XG5cbiAgY29uc3RydWN0b3IgKHJlZGlzKSB7XG4gICAgdGhpcy5yZWRpcyA9IHJlZGlzXG4gIH1cblxuICBsb2NrIChrZXksIHZhbCwgdHRsKSB7XG4gICAgcmV0dXJuIHByb21pc2VSZXRyeShcbiAgICAgIHttaW5UaW1lb3V0OiAxMDAsIHJldHJpZXM6IDEwLCBmYWN0b3I6IDEuNSwgcmFuZG9taXplOiB0cnVlfVxuICAgICAgLCAocmV0cnksIG4pID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0KGtleSwgdmFsLCAnTlgnLCAnUFgnLCB0dGwpLnRoZW4ocmVzID0+IHtcbiAgICAgICAgICBpZiAoIXJlcykge1xuICAgICAgICAgICAgbGV0IGVycm9yID0gbmV3IENoYXRTZXJ2aWNlRXJyb3IoJ3RpbWVvdXQnKVxuICAgICAgICAgICAgcmV0dXJuIHJldHJ5KGVycm9yKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICAgIH1cbiAgICAgICAgfSkuY2F0Y2gocmV0cnkpXG4gICAgICB9KVxuICB9XG5cbiAgdW5sb2NrIChrZXksIHZhbCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnVubG9jayhrZXksIHZhbClcbiAgfVxuXG59XG5cbi8vIFJlZGlzIHNjcmlwdHMuXG5sZXQgbHVhQ29tbWFuZHMgPSB7XG4gIHVubG9jazoge1xuICAgIG51bWJlck9mS2V5czogMSxcbiAgICBsdWE6IGBcbmlmIHJlZGlzLmNhbGwoXCJnZXRcIixLRVlTWzFdKSA9PSBBUkdWWzFdIHRoZW5cbiAgcmV0dXJuIHJlZGlzLmNhbGwoXCJkZWxcIixLRVlTWzFdKVxuZWxzZVxuICByZXR1cm4gMFxuZW5kYFxuICB9LFxuXG4gIG1lc3NhZ2VBZGQ6IHtcbiAgICBudW1iZXJPZktleXM6IDUsXG4gICAgbHVhOiBgXG5sb2NhbCBtc2cgPSBBUkdWWzFdXG5sb2NhbCB0cyA9IEFSR1ZbMl1cblxubG9jYWwgbGFzdE1lc3NhZ2VJZCA9IEtFWVNbMV1cbmxvY2FsIGhpc3RvcnlNYXhTaXplID0gS0VZU1syXVxubG9jYWwgbWVzc2FnZXNJZHMgPSBLRVlTWzNdXG5sb2NhbCBtZXNzYWdlc1RpbWVzdGFtcHMgPSBLRVlTWzRdXG5sb2NhbCBtZXNzYWdlc0hpc3RvcnkgPSBLRVlTWzVdXG5cbmxvY2FsIGlkID0gdG9udW1iZXIocmVkaXMuY2FsbCgnSU5DUicsIGxhc3RNZXNzYWdlSWQpKVxubG9jYWwgbWF4c3ogPSB0b251bWJlcihyZWRpcy5jYWxsKCdHRVQnLCBoaXN0b3J5TWF4U2l6ZSkpXG5cbnJlZGlzLmNhbGwoJ0xQVVNIJywgbWVzc2FnZXNJZHMsIGlkKVxucmVkaXMuY2FsbCgnTFBVU0gnLCBtZXNzYWdlc1RpbWVzdGFtcHMsIHRzKVxucmVkaXMuY2FsbCgnTFBVU0gnLCBtZXNzYWdlc0hpc3RvcnksIG1zZylcblxubG9jYWwgc3ogPSB0b251bWJlcihyZWRpcy5jYWxsKCdMTEVOJywgbWVzc2FnZXNIaXN0b3J5KSlcblxuaWYgc3ogPiBtYXhzeiB0aGVuXG4gIHJlZGlzLmNhbGwoJ1JQT1AnLCBtZXNzYWdlc0lkcylcbiAgcmVkaXMuY2FsbCgnUlBPUCcsIG1lc3NhZ2VzVGltZXN0YW1wcylcbiAgcmVkaXMuY2FsbCgnUlBPUCcsIG1lc3NhZ2VzSGlzdG9yeSlcbmVuZFxuXG5yZXR1cm4ge2lkfWBcbiAgfSxcblxuICBtZXNzYWdlc0dldDoge1xuICAgIG51bWJlck9mS2V5czogNSxcbiAgICBsdWE6IGBcbmxvY2FsIGlkID0gQVJHVlsxXVxubG9jYWwgbWF4bGVuID0gQVJHVlsyXVxuXG5sb2NhbCBsYXN0TWVzc2FnZUlkID0gS0VZU1sxXVxubG9jYWwgaGlzdG9yeU1heFNpemUgPSBLRVlTWzJdXG5sb2NhbCBtZXNzYWdlc0lkcyA9IEtFWVNbM11cbmxvY2FsIG1lc3NhZ2VzVGltZXN0YW1wcyA9IEtFWVNbNF1cbmxvY2FsIG1lc3NhZ2VzSGlzdG9yeSA9IEtFWVNbNV1cblxubG9jYWwgbGFzdGlkID0gdG9udW1iZXIocmVkaXMuY2FsbCgnR0VUJywgbGFzdE1lc3NhZ2VJZCkpXG5sb2NhbCBtYXhzeiA9IHRvbnVtYmVyKHJlZGlzLmNhbGwoJ0dFVCcsIGhpc3RvcnlNYXhTaXplKSlcbmxvY2FsIGlkID0gbWF0aC5taW4oaWQsIGxhc3RpZClcbmxvY2FsIGVuZHAgPSBsYXN0aWQgLSBpZFxubG9jYWwgbGVuID0gbWF0aC5taW4obWF4bGVuLCBlbmRwKVxubG9jYWwgc3RhcnQgPSBtYXRoLm1heCgwLCBlbmRwIC0gbGVuKVxuXG5pZiBzdGFydCA+PSBlbmRwIHRoZW5cbiAgcmV0dXJuIHt9XG5lbmRcblxuZW5kcCA9IGVuZHAgLSAxXG5sb2NhbCBtc2dzID0gcmVkaXMuY2FsbCgnTFJBTkdFJywgbWVzc2FnZXNIaXN0b3J5LCBzdGFydCwgZW5kcClcbmxvY2FsIHRzcyA9IHJlZGlzLmNhbGwoJ0xSQU5HRScsIG1lc3NhZ2VzVGltZXN0YW1wcywgc3RhcnQsIGVuZHApXG5sb2NhbCBpZHMgPSByZWRpcy5jYWxsKCdMUkFOR0UnLCBtZXNzYWdlc0lkcywgc3RhcnQsIGVuZHApXG5cbnJldHVybiB7bXNncywgdHNzLCBpZHN9YFxuICB9LFxuXG4gIGdldFNvY2tldHNUb1Jvb21zOiB7XG4gICAgbnVtYmVyT2ZLZXlzOiAxLFxuICAgIGx1YTogYFxubG9jYWwgcmVzdWx0ID0ge31cbmxvY2FsIHNvY2tldHMgPSBLRVlTWzFdXG5sb2NhbCBwcmVmaXggPSBBUkdWWzFdXG5sb2NhbCBpZHMgPSByZWRpcy5jYWxsKCdIS0VZUycsIHNvY2tldHMpXG5cbmlmIHRhYmxlLmdldG4oaWRzKSA9PSAwIHRoZW5cbiAgbG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZShjanNvbi5udWxsKVxuICByZXR1cm4ge2pzb25SZXN1bHR9XG5lbmRcblxuZm9yIGksIGlkIGluIHBhaXJzKGlkcykgZG9cbiAgbG9jYWwgam9pbmVkID0gcmVkaXMuY2FsbCgnU01FTUJFUlMnLCBwcmVmaXggLi4gaWQpXG4gIHJlc3VsdFtpZF0gPSBqb2luZWRcbmVuZFxuXG5sb2NhbCBqc29uUmVzdWx0ID0gY2pzb24uZW5jb2RlKHJlc3VsdClcbnJldHVybiB7anNvblJlc3VsdH1gXG4gIH0sXG5cbiAgcmVtb3ZlQWxsU29ja2V0c0Zyb21Sb29tOiB7XG4gICAgbnVtYmVyT2ZLZXlzOiAxLFxuICAgIGx1YTogYFxubG9jYWwgcm9vbSA9IEtFWVNbMV1cbmxvY2FsIHByZWZpeCA9IEFSR1ZbMV1cbmxvY2FsIHJvb21OYW1lID0gQVJHVlsyXVxubG9jYWwgaWRzID0gcmVkaXMuY2FsbCgnU01FTUJFUlMnLCByb29tKVxuXG5pZiB0YWJsZS5nZXRuKGlkcykgPT0gMCB0aGVuXG4gIGxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUoY2pzb24ubnVsbClcbiAgcmV0dXJuIHtqc29uUmVzdWx0fVxuZW5kXG5cbnJlZGlzLmNhbGwoJ0RFTCcsIHJvb20pXG5cbmZvciBpLCBpZCBpbiBwYWlycyhpZHMpIGRvXG4gIHJlZGlzLmNhbGwoJ1NSRU0nLCBwcmVmaXggLi4gaWQsIHJvb21OYW1lKVxuZW5kXG5cbmxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUoaWRzKVxucmV0dXJuIHtqc29uUmVzdWx0fWBcbiAgfSxcblxuICByZW1vdmVTb2NrZXQ6IHtcbiAgICBudW1iZXJPZktleXM6IDIsXG4gICAgbHVhOiBgXG5sb2NhbCBpZCA9IEtFWVNbMV1cbmxvY2FsIHNvY2tldHMgPSBLRVlTWzJdXG5sb2NhbCBwcmVmaXggPSBBUkdWWzFdXG5sb2NhbCBzb2NrZXRpZCA9IEFSR1ZbMl1cblxubG9jYWwgcm9vbXMgPSByZWRpcy5jYWxsKCdTTUVNQkVSUycsIGlkKVxucmVkaXMuY2FsbCgnREVMJywgaWQpXG5cbnJlZGlzLmNhbGwoJ0hERUwnLCBzb2NrZXRzLCBzb2NrZXRpZClcbmxvY2FsIG5jb25uZWN0ZWQgPSByZWRpcy5jYWxsKCdITEVOJywgc29ja2V0cylcblxubG9jYWwgcmVtb3ZlZFJvb21zID0ge31cbmxvY2FsIGpvaW5lZFNvY2tldHMgPSB7fVxuXG5mb3IgaSwgcm9vbSBpbiBwYWlycyhyb29tcykgZG9cbiAgbG9jYWwgaXNtZW1iZXIgPSByZWRpcy5jYWxsKCdTSVNNRU1CRVInLCBwcmVmaXggLi4gcm9vbSwgc29ja2V0aWQpXG4gIGlmIGlzbWVtYmVyID09IDEgdGhlblxuICAgIHJlZGlzLmNhbGwoJ1NSRU0nLCBwcmVmaXggLi4gcm9vbSwgc29ja2V0aWQpXG4gICAgbG9jYWwgbmpvaW5lZCA9IHJlZGlzLmNhbGwoJ1NDQVJEJywgcHJlZml4IC4uIHJvb20pXG4gICAgdGFibGUuaW5zZXJ0KHJlbW92ZWRSb29tcywgcm9vbSlcbiAgICB0YWJsZS5pbnNlcnQoam9pbmVkU29ja2V0cywgbmpvaW5lZClcbiAgZW5kXG5lbmRcblxuaWYgdGFibGUuZ2V0bihyZW1vdmVkUm9vbXMpID09IDAgb3IgdGFibGUuZ2V0bihyb29tcykgPT0gMCB0aGVuXG4gIGxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUoe2Nqc29uLm51bGwsIGNqc29uLm51bGwsIG5jb25uZWN0ZWR9KVxuICByZXR1cm4ge2pzb25SZXN1bHR9XG5lbmRcblxubG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZSh7cmVtb3ZlZFJvb21zLCBqb2luZWRTb2NrZXRzLCBuY29ubmVjdGVkfSlcbnJldHVybiB7anNvblJlc3VsdH1gXG4gIH1cblxufVxuXG4vLyBJbXBsZW1lbnRzIHN0YXRlIEFQSSBsaXN0cyBtYW5hZ2VtZW50LlxuY2xhc3MgTGlzdHNTdGF0ZVJlZGlzIHtcblxuICBtYWtlS2V5TmFtZSAoa2V5TmFtZSkge1xuICAgIHJldHVybiBgJHtuYW1lc3BhY2V9OiR7dGhpcy5wcmVmaXh9Onske3RoaXMubmFtZX19OiR7a2V5TmFtZX1gXG4gIH1cblxuICBjaGVja0xpc3QgKGxpc3ROYW1lLCBudW0sIGxpbWl0KSB7XG4gICAgaWYgKCF0aGlzLmhhc0xpc3QobGlzdE5hbWUpKSB7XG4gICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcignbm9MaXN0JywgbGlzdE5hbWUpXG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgfVxuICAgIGlmIChsaXN0TmFtZSA9PT0gJ3VzZXJsaXN0Jykge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgfVxuICAgIHJldHVybiB0aGlzLnJlZGlzLnNjYXJkKGxpc3ROYW1lKS50aGVuKHN6ID0+IHtcbiAgICAgIGlmIChzeiArIG51bSA+IGxpbWl0KSB7XG4gICAgICAgIGxldCBlcnJvciA9IG5ldyBDaGF0U2VydmljZUVycm9yKCdsaXN0TGltaXRFeGNlZWRlZCcsIGxpc3ROYW1lKVxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgYWRkVG9MaXN0IChsaXN0TmFtZSwgZWxlbXMsIGxpbWl0KSB7XG4gICAgbGV0IG51bSA9IGVsZW1zLmxlbmd0aFxuICAgIHJldHVybiB0aGlzLmNoZWNrTGlzdChsaXN0TmFtZSwgbnVtLCBsaW1pdClcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc2FkZCh0aGlzLm1ha2VLZXlOYW1lKGxpc3ROYW1lKSwgZWxlbXMpKVxuICB9XG5cbiAgcmVtb3ZlRnJvbUxpc3QgKGxpc3ROYW1lLCBlbGVtcykge1xuICAgIHJldHVybiB0aGlzLmNoZWNrTGlzdChsaXN0TmFtZSlcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc3JlbSh0aGlzLm1ha2VLZXlOYW1lKGxpc3ROYW1lKSwgZWxlbXMpKVxuICB9XG5cbiAgZ2V0TGlzdCAobGlzdE5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5jaGVja0xpc3QobGlzdE5hbWUpXG4gICAgICAudGhlbigoKSA9PiB0aGlzLnJlZGlzLnNtZW1iZXJzKHRoaXMubWFrZUtleU5hbWUobGlzdE5hbWUpKSlcbiAgfVxuXG4gIGhhc0luTGlzdCAobGlzdE5hbWUsIGVsZW0pIHtcbiAgICByZXR1cm4gdGhpcy5jaGVja0xpc3QobGlzdE5hbWUpXG4gICAgICAudGhlbigoKSA9PiB0aGlzLnJlZGlzLnNpc21lbWJlcih0aGlzLm1ha2VLZXlOYW1lKGxpc3ROYW1lKSwgZWxlbSkpXG4gICAgICAudGhlbihkYXRhID0+IFByb21pc2UucmVzb2x2ZShCb29sZWFuKGRhdGEpKSlcbiAgfVxuXG4gIHdoaXRlbGlzdE9ubHlTZXQgKG1vZGUpIHtcbiAgICBsZXQgd2hpdGVsaXN0T25seSA9IG1vZGUgPyB0cnVlIDogJydcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnd2hpdGVsaXN0TW9kZScpLCB3aGl0ZWxpc3RPbmx5KVxuICB9XG5cbiAgd2hpdGVsaXN0T25seUdldCAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdE1vZGUnKSlcbiAgICAgIC50aGVuKGRhdGEgPT4gUHJvbWlzZS5yZXNvbHZlKEJvb2xlYW4oZGF0YSkpKVxuICB9XG5cbn1cblxuLy8gSW1wbGVtZW50cyByb29tIHN0YXRlIEFQSS5cbmNsYXNzIFJvb21TdGF0ZVJlZGlzIGV4dGVuZHMgTGlzdHNTdGF0ZVJlZGlzIHtcblxuICBjb25zdHJ1Y3RvciAoc2VydmVyLCByb29tTmFtZSkge1xuICAgIHN1cGVyKClcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlclxuICAgIHRoaXMucm9vbU5hbWUgPSByb29tTmFtZVxuICAgIHRoaXMubmFtZSA9IHRoaXMucm9vbU5hbWVcbiAgICB0aGlzLmhpc3RvcnlNYXhHZXRNZXNzYWdlcyA9IHRoaXMuc2VydmVyLmhpc3RvcnlNYXhHZXRNZXNzYWdlc1xuICAgIHRoaXMucmVkaXMgPSB0aGlzLnNlcnZlci5yZWRpc1xuICAgIHRoaXMuZXhpdHNFcnJvck5hbWUgPSAncm9vbUV4aXN0cydcbiAgICB0aGlzLnByZWZpeCA9ICdyb29tcydcbiAgICBtaXhpbih0aGlzLCBTdGF0ZU9wZXJhdGlvbnMsIHRoaXMubmFtZSwgdGhpcy5leGl0c0Vycm9yTmFtZSwgdGhpcy5yZWRpcyxcbiAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lLmJpbmQodGhpcyksIHRoaXMuc3RhdGVSZXNldC5iaW5kKHRoaXMpKVxuICB9XG5cbiAgc3RhdGVSZXNldCAoc3RhdGUpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IHt9XG4gICAgbGV0IHsgd2hpdGVsaXN0LCBibGFja2xpc3QsIGFkbWlubGlzdCxcbiAgICAgICAgICB3aGl0ZWxpc3RPbmx5LCBvd25lciwgaGlzdG9yeU1heFNpemUsXG4gICAgICAgICAgZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzID0gdGhpcy5zZXJ2ZXIuZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzLFxuICAgICAgICAgIGVuYWJsZVVzZXJsaXN0VXBkYXRlcyA9IHRoaXMuc2VydmVyLmVuYWJsZVVzZXJsaXN0VXBkYXRlc1xuICAgICAgICB9ID0gc3RhdGVcbiAgICBpZiAoIW93bmVyKSB7IG93bmVyID0gJycgfVxuICAgIHJldHVybiBQcm9taXNlLmFsbChbXG4gICAgICBpbml0U2V0KHRoaXMucmVkaXMsIHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdCcpLCB3aGl0ZWxpc3QpLFxuICAgICAgaW5pdFNldCh0aGlzLnJlZGlzLCB0aGlzLm1ha2VLZXlOYW1lKCdibGFja2xpc3QnKSwgYmxhY2tsaXN0KSxcbiAgICAgIGluaXRTZXQodGhpcy5yZWRpcywgdGhpcy5tYWtlS2V5TmFtZSgnYWRtaW5saXN0JyksIGFkbWlubGlzdCksXG4gICAgICBpbml0U2V0KHRoaXMucmVkaXMsIHRoaXMubWFrZUtleU5hbWUoJ3VzZXJsaXN0JyksIG51bGwpLFxuICAgICAgdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JykpLFxuICAgICAgdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNUaW1lc3RhbXBzJykpLFxuICAgICAgdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNJZHMnKSksXG4gICAgICB0aGlzLnJlZGlzLmRlbCh0aGlzLm1ha2VLZXlOYW1lKCd1c2Vyc3NlZW4nKSksXG4gICAgICB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdsYXN0TWVzc2FnZUlkJyksIDApLFxuICAgICAgdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnb3duZXInKSwgb3duZXIpLFxuICAgICAgdGhpcy53aGl0ZWxpc3RPbmx5U2V0KHdoaXRlbGlzdE9ubHkpLFxuICAgICAgdGhpcy5hY2Nlc3NMaXN0c1VwZGF0ZXNTZXQoZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzKSxcbiAgICAgIHRoaXMudXNlcmxpc3RVcGRhdGVzU2V0KGVuYWJsZVVzZXJsaXN0VXBkYXRlcyksXG4gICAgICB0aGlzLmhpc3RvcnlNYXhTaXplU2V0KGhpc3RvcnlNYXhTaXplKVxuICAgIF0pLnJldHVybigpXG4gIH1cblxuICBoYXNMaXN0IChsaXN0TmFtZSkge1xuICAgIHJldHVybiBsaXN0TmFtZSA9PT0gJ2FkbWlubGlzdCcgfHwgbGlzdE5hbWUgPT09ICd3aGl0ZWxpc3QnIHx8XG4gICAgICBsaXN0TmFtZSA9PT0gJ2JsYWNrbGlzdCcgfHwgbGlzdE5hbWUgPT09ICd1c2VybGlzdCdcbiAgfVxuXG4gIG93bmVyR2V0ICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXQodGhpcy5tYWtlS2V5TmFtZSgnb3duZXInKSlcbiAgfVxuXG4gIG93bmVyU2V0IChvd25lcikge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdvd25lcicpLCBvd25lcilcbiAgfVxuXG4gIGFjY2Vzc0xpc3RzVXBkYXRlc1NldCAoZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzKSB7XG4gICAgZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzID0gZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzID8gdHJ1ZSA6ICcnXG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0KHRoaXMubWFrZUtleU5hbWUoJ2VuYWJsZUFjY2Vzc0xpc3RzVXBkYXRlcycpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMpXG4gIH1cblxuICBhY2Nlc3NMaXN0c1VwZGF0ZXNHZXQgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMnKSlcbiAgICAgIC50aGVuKGRhdGEgPT4gUHJvbWlzZS5yZXNvbHZlKEJvb2xlYW4oZGF0YSkpKVxuICB9XG5cbiAgdXNlcmxpc3RVcGRhdGVzU2V0IChlbmFibGVVc2VybGlzdFVwZGF0ZXMpIHtcbiAgICBlbmFibGVVc2VybGlzdFVwZGF0ZXMgPSBlbmFibGVVc2VybGlzdFVwZGF0ZXMgPyB0cnVlIDogJydcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnZW5hYmxlVXNlcmxpc3RVcGRhdGVzJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZVVzZXJsaXN0VXBkYXRlcylcbiAgfVxuXG4gIHVzZXJsaXN0VXBkYXRlc0dldCAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ2VuYWJsZVVzZXJsaXN0VXBkYXRlcycpKVxuICAgICAgLnRoZW4oZGF0YSA9PiBQcm9taXNlLnJlc29sdmUoQm9vbGVhbihkYXRhKSkpXG4gIH1cblxuICBoaXN0b3J5TWF4U2l6ZVNldCAoaGlzdG9yeU1heFNpemUpIHtcbiAgICBsZXQgbGltaXQgPSBoaXN0b3J5TWF4U2l6ZVxuICAgIGlmICghKF8uaXNOdW1iZXIoaGlzdG9yeU1heFNpemUpICYmIGhpc3RvcnlNYXhTaXplID49IDApKSB7XG4gICAgICBsaW1pdCA9IHRoaXMuc2VydmVyLmhpc3RvcnlNYXhTaXplXG4gICAgfVxuICAgIGlmIChsaW1pdCA9PT0gMCkge1xuICAgICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgICAuc2V0KHRoaXMubWFrZUtleU5hbWUoJ2hpc3RvcnlNYXhTaXplJyksIGxpbWl0KVxuICAgICAgICAuZGVsKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpKVxuICAgICAgICAuZGVsKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzVGltZXN0YW1wcycpKVxuICAgICAgICAuZGVsKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJykpXG4gICAgICAgIC5leGVjKClcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IGxhc3QgPSBsaW1pdCAtIDFcbiAgICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgICAgLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdoaXN0b3J5TWF4U2l6ZScpLCBsaW1pdClcbiAgICAgICAgLmx0cmltKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpLCAwLCBsYXN0KVxuICAgICAgICAubHRyaW0odGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNUaW1lc3RhbXBzJyksIDAsIGxhc3QpXG4gICAgICAgIC5sdHJpbSh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0lkcycpLCAwLCBsYXN0KVxuICAgICAgICAuZXhlYygpXG4gICAgfVxuICB9XG5cbiAgaGlzdG9yeUluZm8gKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5nZXQodGhpcy5tYWtlS2V5TmFtZSgnaGlzdG9yeU1heFNpemUnKSlcbiAgICAgIC5sbGVuKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpKVxuICAgICAgLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdsYXN0TWVzc2FnZUlkJykpXG4gICAgICAuZXhlYygpXG4gICAgICAuc3ByZWFkKChbLCBoaXN0b3J5TWF4U2l6ZV0sIFssIGhpc3RvcnlTaXplXSwgWywgbGFzdE1lc3NhZ2VJZF0pID0+IHtcbiAgICAgICAgaGlzdG9yeVNpemUgPSBwYXJzZUludChoaXN0b3J5U2l6ZSlcbiAgICAgICAgaGlzdG9yeU1heFNpemUgPSBwYXJzZUZsb2F0KGhpc3RvcnlNYXhTaXplKVxuICAgICAgICBsYXN0TWVzc2FnZUlkID0gcGFyc2VJbnQobGFzdE1lc3NhZ2VJZClcbiAgICAgICAgbGV0IGluZm8gPSB7IGhpc3RvcnlTaXplLFxuICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeU1heFNpemUsXG4gICAgICAgICAgICAgICAgICAgICBoaXN0b3J5TWF4R2V0TWVzc2FnZXM6IHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzLFxuICAgICAgICAgICAgICAgICAgICAgbGFzdE1lc3NhZ2VJZCB9XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoaW5mbylcbiAgICAgIH0pXG4gIH1cblxuICBnZXRDb21tb25Vc2VycyAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2RpZmYodGhpcy5tYWtlS2V5TmFtZSgndXNlcmxpc3QnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3QnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lKCdhZG1pbmxpc3QnKSlcbiAgfVxuXG4gIG1lc3NhZ2VBZGQgKG1zZykge1xuICAgIGxldCB0aW1lc3RhbXAgPSBfLm5vdygpXG4gICAgbGV0IHNtc2cgPSBKU09OLnN0cmluZ2lmeShtc2cpXG4gICAgcmV0dXJuIHRoaXMucmVkaXMubWVzc2FnZUFkZChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2xhc3RNZXNzYWdlSWQnKSwgdGhpcy5tYWtlS2V5TmFtZSgnaGlzdG9yeU1heFNpemUnKSxcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJyksIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzVGltZXN0YW1wcycpLFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JyksIHNtc2csIHRpbWVzdGFtcClcbiAgICAgIC5zcHJlYWQoaWQgPT4ge1xuICAgICAgICBtc2cuaWQgPSBpZFxuICAgICAgICBtc2cudGltZXN0YW1wID0gdGltZXN0YW1wXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobXNnKVxuICAgICAgfSlcbiAgfVxuXG4gIGNvbnZlcnRNZXNzYWdlcyAobXNncywgdHNzLCBpZHMpIHtcbiAgICBsZXQgZGF0YSA9IFtdXG4gICAgaWYgKCFtc2dzKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGRhdGEpXG4gICAgfVxuICAgIGZvciAobGV0IGlkeCA9IDA7IGlkeCA8IG1zZ3MubGVuZ3RoOyBpZHgrKykge1xuICAgICAgbGV0IG1zZyA9IG1zZ3NbaWR4XVxuICAgICAgbGV0IG9iaiA9IEpTT04ucGFyc2UobXNnLCAoa2V5LCB2YWwpID0+IHtcbiAgICAgICAgaWYgKHZhbCAmJiB2YWwudHlwZSA9PT0gJ0J1ZmZlcicpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IEJ1ZmZlcih2YWwuZGF0YSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gdmFsXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICBvYmoudGltZXN0YW1wID0gcGFyc2VJbnQodHNzW2lkeF0pXG4gICAgICBvYmouaWQgPSBwYXJzZUludChpZHNbaWR4XSlcbiAgICAgIGRhdGFbaWR4XSA9IG9ialxuICAgIH1cbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGRhdGEpXG4gIH1cblxuICBtZXNzYWdlc0dldFJlY2VudCAoKSB7XG4gICAgaWYgKHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzIDw9IDApIHsgcmV0dXJuIFByb21pc2UucmVzb2x2ZShbXSkgfVxuICAgIGxldCBsaW1pdCA9IHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzIC0gMVxuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5scmFuZ2UodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JyksIDAsIGxpbWl0KVxuICAgICAgLmxyYW5nZSh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc1RpbWVzdGFtcHMnKSwgMCwgbGltaXQpXG4gICAgICAubHJhbmdlKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJyksIDAsIGxpbWl0KVxuICAgICAgLmV4ZWMoKVxuICAgICAgLnNwcmVhZCgoWywgbXNnc10sIFssIHRzc10sIFssIGlkc10pID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udmVydE1lc3NhZ2VzKG1zZ3MsIHRzcywgaWRzKVxuICAgICAgfSlcbiAgfVxuXG4gIG1lc3NhZ2VzR2V0IChpZCwgbWF4TWVzc2FnZXMgPSB0aGlzLmhpc3RvcnlNYXhHZXRNZXNzYWdlcykge1xuICAgIGlmIChtYXhNZXNzYWdlcyA8PSAwKSB7IHJldHVybiBQcm9taXNlLnJlc29sdmUoW10pIH1cbiAgICBpZCA9IF8ubWF4KFswLCBpZF0pXG4gICAgcmV0dXJuIHRoaXMucmVkaXMubWVzc2FnZXNHZXQoXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdsYXN0TWVzc2FnZUlkJyksIHRoaXMubWFrZUtleU5hbWUoJ2hpc3RvcnlNYXhTaXplJyksXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0lkcycpLCB0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc1RpbWVzdGFtcHMnKSxcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpLCBpZCwgbWF4TWVzc2FnZXMpXG4gICAgICAuc3ByZWFkKChtc2dzLCB0c3MsIGlkcykgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jb252ZXJ0TWVzc2FnZXMobXNncywgdHNzLCBpZHMpXG4gICAgICB9KVxuICB9XG5cbiAgdXNlclNlZW5HZXQgKHVzZXJOYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgLmhnZXQodGhpcy5tYWtlS2V5TmFtZSgndXNlcnNzZWVuJyksIHVzZXJOYW1lKVxuICAgICAgLnNpc21lbWJlcih0aGlzLm1ha2VLZXlOYW1lKCd1c2VybGlzdCcpLCB1c2VyTmFtZSlcbiAgICAgIC5leGVjKClcbiAgICAgIC5zcHJlYWQoKFssIHRzXSwgWywgaXNqb2luZWRdKSA9PiB7XG4gICAgICAgIGxldCBqb2luZWQgPSBCb29sZWFuKGlzam9pbmVkKVxuICAgICAgICBsZXQgdGltZXN0YW1wID0gdHMgPyBwYXJzZUludCh0cykgOiBudWxsXG4gICAgICAgIHJldHVybiB7am9pbmVkLCB0aW1lc3RhbXB9XG4gICAgICB9KVxuICB9XG5cbiAgdXNlclNlZW5VcGRhdGUgKHVzZXJOYW1lKSB7XG4gICAgbGV0IHRpbWVzdGFtcCA9IF8ubm93KClcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5oc2V0KHRoaXMubWFrZUtleU5hbWUoJ3VzZXJzc2VlbicpLCB1c2VyTmFtZSwgdGltZXN0YW1wKVxuICB9XG5cbn1cblxuLy8gSW1wbGVtZW50cyBkaXJlY3QgbWVzc2FnaW5nIHN0YXRlIEFQSS5cbmNsYXNzIERpcmVjdE1lc3NhZ2luZ1N0YXRlUmVkaXMgZXh0ZW5kcyBMaXN0c1N0YXRlUmVkaXMge1xuXG4gIGNvbnN0cnVjdG9yIChzZXJ2ZXIsIHVzZXJOYW1lKSB7XG4gICAgc3VwZXIoKVxuICAgIHRoaXMuc2VydmVyID0gc2VydmVyXG4gICAgdGhpcy51c2VyTmFtZSA9IHVzZXJOYW1lXG4gICAgdGhpcy5uYW1lID0gdGhpcy51c2VyTmFtZVxuICAgIHRoaXMucHJlZml4ID0gJ3VzZXJzJ1xuICAgIHRoaXMuZXhpdHNFcnJvck5hbWUgPSAndXNlckV4aXN0cydcbiAgICB0aGlzLnJlZGlzID0gdGhpcy5zZXJ2ZXIucmVkaXNcbiAgICBtaXhpbih0aGlzLCBTdGF0ZU9wZXJhdGlvbnMsIHRoaXMubmFtZSwgdGhpcy5leGl0c0Vycm9yTmFtZSwgdGhpcy5yZWRpcyxcbiAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lLmJpbmQodGhpcyksIHRoaXMuc3RhdGVSZXNldC5iaW5kKHRoaXMpKVxuICB9XG5cbiAgaGFzTGlzdCAobGlzdE5hbWUpIHtcbiAgICByZXR1cm4gbGlzdE5hbWUgPT09ICd3aGl0ZWxpc3QnIHx8IGxpc3ROYW1lID09PSAnYmxhY2tsaXN0J1xuICB9XG5cbiAgc3RhdGVSZXNldCAoc3RhdGUpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IHt9XG4gICAgbGV0IHsgd2hpdGVsaXN0LCBibGFja2xpc3QsIHdoaXRlbGlzdE9ubHkgfSA9IHN0YXRlXG4gICAgd2hpdGVsaXN0T25seSA9IHdoaXRlbGlzdE9ubHkgPyB0cnVlIDogJydcbiAgICByZXR1cm4gUHJvbWlzZS5hbGwoW1xuICAgICAgaW5pdFNldCh0aGlzLnJlZGlzLCB0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3QnKSwgd2hpdGVsaXN0KSxcbiAgICAgIGluaXRTZXQodGhpcy5yZWRpcywgdGhpcy5tYWtlS2V5TmFtZSgnYmxhY2tsaXN0JyksIGJsYWNrbGlzdCksXG4gICAgICB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3RNb2RlJyksIHdoaXRlbGlzdE9ubHkpXG4gICAgXSkucmV0dXJuKClcbiAgfVxuXG59XG5cbi8vIEltcGxlbWVudHMgdXNlciBzdGF0ZSBBUEkuXG5jbGFzcyBVc2VyU3RhdGVSZWRpcyB7XG5cbiAgY29uc3RydWN0b3IgKHNlcnZlciwgdXNlck5hbWUpIHtcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlclxuICAgIHRoaXMudXNlck5hbWUgPSB1c2VyTmFtZVxuICAgIHRoaXMubmFtZSA9IHRoaXMudXNlck5hbWVcbiAgICB0aGlzLnByZWZpeCA9ICd1c2VycydcbiAgICB0aGlzLnJlZGlzID0gdGhpcy5zZXJ2ZXIucmVkaXNcbiAgICBtaXhpbih0aGlzLCBMb2NrT3BlcmF0aW9ucywgdGhpcy5yZWRpcylcbiAgfVxuXG4gIG1ha2VLZXlOYW1lIChrZXlOYW1lKSB7XG4gICAgcmV0dXJuIGAke25hbWVzcGFjZX06JHt0aGlzLnByZWZpeH06eyR7dGhpcy5uYW1lfX06JHtrZXlOYW1lfWBcbiAgfVxuXG4gIG1ha2VTb2NrZXRUb1Jvb21zIChpZCA9ICcnKSB7XG4gICAgcmV0dXJuIHRoaXMubWFrZUtleU5hbWUoYHNvY2tldHNUb1Jvb21zOiR7aWR9YClcbiAgfVxuXG4gIG1ha2VSb29tVG9Tb2NrZXRzIChyb29tID0gJycpIHtcbiAgICByZXR1cm4gdGhpcy5tYWtlS2V5TmFtZShgcm9vbXNUb1NvY2tldHM6JHtyb29tfWApXG4gIH1cblxuICBtYWtlUm9vbUxvY2sgKHJvb20pIHtcbiAgICByZXR1cm4gdGhpcy5tYWtlS2V5TmFtZShgcm9vbUxvY2s6JHtyb29tfWApXG4gIH1cblxuICBhZGRTb2NrZXQgKGlkLCB1aWQpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5tdWx0aSgpXG4gICAgICAuaHNldCh0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJyksIGlkLCB1aWQpXG4gICAgICAuaGxlbih0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJykpXG4gICAgICAuZXhlYygpXG4gICAgICAuc3ByZWFkKChfLCBbLCBuY29ubmVjdGVkXSkgPT4gUHJvbWlzZS5yZXNvbHZlKG5jb25uZWN0ZWQpKVxuICB9XG5cbiAgZ2V0QWxsU29ja2V0cyAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGtleXModGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpKVxuICB9XG5cbiAgZ2V0U29ja2V0c1RvSW5zdGFuY2UgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmhnZXRhbGwodGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpKVxuICB9XG5cbiAgZ2V0Um9vbVRvU29ja2V0cyAocm9vbU5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zbWVtYmVycyh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgfVxuXG4gIGdldFNvY2tldHNUb1Jvb21zICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXRTb2NrZXRzVG9Sb29tcyhcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ3NvY2tldHMnKSwgdGhpcy5tYWtlU29ja2V0VG9Sb29tcygpKVxuICAgICAgLnNwcmVhZChyZXN1bHQgPT4ge1xuICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UocmVzdWx0KSB8fCB7fVxuICAgICAgICBmb3IgKGxldCBbaywgdl0gb2YgXy50b1BhaXJzKGRhdGEpKSB7XG4gICAgICAgICAgaWYgKF8uaXNFbXB0eSh2KSkgeyBkYXRhW2tdID0gW10gfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGF0YSlcbiAgICAgIH0pXG4gIH1cblxuICBhZGRTb2NrZXRUb1Jvb20gKGlkLCByb29tTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5zYWRkKHRoaXMubWFrZVNvY2tldFRvUm9vbXMoaWQpLCByb29tTmFtZSlcbiAgICAgIC5zYWRkKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpLCBpZClcbiAgICAgIC5zY2FyZCh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgICAgIC5leGVjKClcbiAgICAgIC50aGVuKChbLCAsIFssIG5qb2luZWRdXSkgPT4gUHJvbWlzZS5yZXNvbHZlKG5qb2luZWQpKVxuICB9XG5cbiAgcmVtb3ZlU29ja2V0RnJvbVJvb20gKGlkLCByb29tTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5zY2FyZCh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgICAgIC5zcmVtKHRoaXMubWFrZVNvY2tldFRvUm9vbXMoaWQpLCByb29tTmFtZSlcbiAgICAgIC5zcmVtKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpLCBpZClcbiAgICAgIC5zY2FyZCh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgICAgIC5leGVjKClcbiAgICAgIC50aGVuKChbWywgd2Fzam9pbmVkXSwgLCAsIFssIG5qb2luZWRdXSkgPT4ge1xuICAgICAgICBsZXQgaGFzQ2hhbmdlZCA9IG5qb2luZWQgIT09IHdhc2pvaW5lZFxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKFtuam9pbmVkLCBoYXNDaGFuZ2VkXSlcbiAgICAgIH0pXG4gIH1cblxuICByZW1vdmVBbGxTb2NrZXRzRnJvbVJvb20gKHJvb21OYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMucmVtb3ZlQWxsU29ja2V0c0Zyb21Sb29tKFxuICAgICAgdGhpcy5tYWtlUm9vbVRvU29ja2V0cyhyb29tTmFtZSksIHRoaXMubWFrZVNvY2tldFRvUm9vbXMoKSwgcm9vbU5hbWUpXG4gICAgICAuc3ByZWFkKHJlc3VsdCA9PiBQcm9taXNlLnJlc29sdmUoSlNPTi5wYXJzZShyZXN1bHQpKSlcbiAgfVxuXG4gIHJlbW92ZVNvY2tldCAoaWQpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5yZW1vdmVTb2NrZXQoXG4gICAgICB0aGlzLm1ha2VTb2NrZXRUb1Jvb21zKGlkKSwgdGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpLFxuICAgICAgdGhpcy5tYWtlUm9vbVRvU29ja2V0cygpLCBpZClcbiAgICAgIC5zcHJlYWQocmVzdWx0ID0+IFByb21pc2UucmVzb2x2ZShKU09OLnBhcnNlKHJlc3VsdCkpKVxuICB9XG5cbiAgbG9ja1RvUm9vbSAocm9vbU5hbWUsIHR0bCkge1xuICAgIHJldHVybiB1aWQoMTgpLnRoZW4odmFsID0+IHtcbiAgICAgIGxldCBzdGFydCA9IF8ubm93KClcbiAgICAgIHJldHVybiB0aGlzLmxvY2sodGhpcy5tYWtlUm9vbUxvY2socm9vbU5hbWUpLCB2YWwsIHR0bCkudGhlbigoKSA9PiB7XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKS5kaXNwb3NlcigoKSA9PiB7XG4gICAgICAgICAgaWYgKHN0YXJ0ICsgdHRsIDwgXy5ub3coKSkge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZW1pdChcbiAgICAgICAgICAgICAgJ2xvY2tUaW1lRXhjZWVkZWQnLCB2YWwsIHt1c2VyTmFtZTogdGhpcy51c2VyTmFtZSwgcm9vbU5hbWV9KVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gdGhpcy51bmxvY2sodGhpcy5tYWtlUm9vbUxvY2socm9vbU5hbWUpLCB2YWwpXG4gICAgICAgIH0pXG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxufVxuXG4vLyBJbXBsZW1lbnRzIGdsb2JhbCBzdGF0ZSBBUEkuXG5jbGFzcyBSZWRpc1N0YXRlIHtcblxuICBjb25zdHJ1Y3RvciAoc2VydmVyLCBvcHRpb25zKSB7XG4gICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXJcbiAgICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zXG4gICAgdGhpcy5jbG9zZWQgPSBmYWxzZVxuICAgIGlmICh0aGlzLm9wdGlvbnMudXNlQ2x1c3Rlcikge1xuICAgICAgdGhpcy5yZWRpcyA9IG5ldyBSZWRpcy5DbHVzdGVyKC4uLnRoaXMub3B0aW9ucy5yZWRpc09wdGlvbnMpXG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCByZWRpc09wdGlvbnMgPSBfLmNhc3RBcnJheSh0aGlzLm9wdGlvbnMucmVkaXNPcHRpb25zKVxuICAgICAgdGhpcy5yZWRpcyA9IG5ldyBSZWRpcyguLi5yZWRpc09wdGlvbnMpXG4gICAgfVxuICAgIHRoaXMuUm9vbVN0YXRlID0gUm9vbVN0YXRlUmVkaXNcbiAgICB0aGlzLlVzZXJTdGF0ZSA9IFVzZXJTdGF0ZVJlZGlzXG4gICAgdGhpcy5EaXJlY3RNZXNzYWdpbmdTdGF0ZSA9IERpcmVjdE1lc3NhZ2luZ1N0YXRlUmVkaXNcbiAgICB0aGlzLmxvY2tUVEwgPSB0aGlzLm9wdGlvbnMubG9ja1RUTCB8fCAxMDAwMFxuICAgIHRoaXMuaW5zdGFuY2VVSUQgPSB0aGlzLnNlcnZlci5pbnN0YW5jZVVJRFxuICAgIHRoaXMuc2VydmVyLnJlZGlzID0gdGhpcy5yZWRpc1xuICAgIGZvciAobGV0IFtjbWQsIGRlZl0gb2YgXy50b1BhaXJzKGx1YUNvbW1hbmRzKSkge1xuICAgICAgdGhpcy5yZWRpcy5kZWZpbmVDb21tYW5kKGNtZCwge1xuICAgICAgICBudW1iZXJPZktleXM6IGRlZi5udW1iZXJPZktleXMsXG4gICAgICAgIGx1YTogZGVmLmx1YVxuICAgICAgfSlcbiAgICB9XG4gIH1cblxuICBtYWtlS2V5TmFtZSAocHJlZml4LCBuYW1lLCBrZXlOYW1lKSB7XG4gICAgcmV0dXJuIGAke25hbWVzcGFjZX06JHtwcmVmaXh9Onske25hbWV9fToke2tleU5hbWV9YFxuICB9XG5cbiAgaGFzUm9vbSAobmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdyb29tcycsIG5hbWUsICdpc0luaXQnKSlcbiAgfVxuXG4gIGhhc1VzZXIgKG5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXQodGhpcy5tYWtlS2V5TmFtZSgndXNlcnMnLCBuYW1lLCAnaXNJbml0JykpXG4gIH1cblxuICBjbG9zZSAoKSB7XG4gICAgdGhpcy5jbG9zZWQgPSB0cnVlXG4gICAgcmV0dXJuIHRoaXMucmVkaXMucXVpdCgpLnJldHVybigpXG4gIH1cblxuICBnZXRSb29tIChuYW1lLCBpc1ByZWRpY2F0ZSA9IGZhbHNlKSB7XG4gICAgbGV0IHJvb20gPSBuZXcgUm9vbSh0aGlzLnNlcnZlciwgbmFtZSlcbiAgICByZXR1cm4gdGhpcy5oYXNSb29tKG5hbWUpLnRoZW4oZXhpc3RzID0+IHtcbiAgICAgIGlmICghZXhpc3RzKSB7XG4gICAgICAgIGlmIChpc1ByZWRpY2F0ZSkge1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcignbm9Sb29tJywgbmFtZSlcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUocm9vbSlcbiAgICB9KVxuICB9XG5cbiAgYWRkUm9vbSAobmFtZSwgc3RhdGUpIHtcbiAgICBsZXQgcm9vbSA9IG5ldyBSb29tKHRoaXMuc2VydmVyLCBuYW1lKVxuICAgIHJldHVybiByb29tLmluaXRTdGF0ZShzdGF0ZSkucmV0dXJuKHJvb20pXG4gIH1cblxuICByZW1vdmVSb29tIChuYW1lKSB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gIH1cblxuICBhZGRTb2NrZXQgKGlkLCB1c2VyTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmhzZXQoXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdpbnN0YW5jZXMnLCB0aGlzLmluc3RhbmNlVUlELCAnc29ja2V0cycpLCBpZCwgdXNlck5hbWUpXG4gIH1cblxuICByZW1vdmVTb2NrZXQgKGlkKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGRlbChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHRoaXMuaW5zdGFuY2VVSUQsICdzb2NrZXRzJyksIGlkKVxuICB9XG5cbiAgZ2V0SW5zdGFuY2VTb2NrZXRzICh1aWQgPSB0aGlzLmluc3RhbmNlVUlEKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGdldGFsbCh0aGlzLm1ha2VLZXlOYW1lKCdpbnN0YW5jZXMnLCB1aWQsICdzb2NrZXRzJykpXG4gIH1cblxuICB1cGRhdGVIZWFydGJlYXQgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNldChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHRoaXMuaW5zdGFuY2VVSUQsICdoZWFydGJlYXQnKSwgXy5ub3coKSlcbiAgICAgIC5jYXRjaFJldHVybigpXG4gIH1cblxuICBnZXRJbnN0YW5jZUhlYXJ0YmVhdCAodWlkID0gdGhpcy5pbnN0YW5jZVVJRCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdpbnN0YW5jZXMnLCB1aWQsICdoZWFydGJlYXQnKSlcbiAgICAgIC50aGVuKHRzID0+IHRzID8gcGFyc2VJbnQodHMpIDogbnVsbClcbiAgfVxuXG4gIGdldE9yQWRkVXNlciAobmFtZSwgc3RhdGUpIHtcbiAgICBsZXQgdXNlciA9IG5ldyBVc2VyKHRoaXMuc2VydmVyLCBuYW1lKVxuICAgIHJldHVybiB0aGlzLmhhc1VzZXIobmFtZSkudGhlbihleGlzdHMgPT4ge1xuICAgICAgaWYgKCFleGlzdHMpIHtcbiAgICAgICAgcmV0dXJuIHVzZXIuaW5pdFN0YXRlKHN0YXRlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICB9XG4gICAgfSkuY2F0Y2goQ2hhdFNlcnZpY2VFcnJvciwgZSA9PiB1c2VyKVxuICAgICAgLnJldHVybih1c2VyKVxuICB9XG5cbiAgZ2V0VXNlciAobmFtZSwgaXNQcmVkaWNhdGUgPSBmYWxzZSkge1xuICAgIGxldCB1c2VyID0gbmV3IFVzZXIodGhpcy5zZXJ2ZXIsIG5hbWUpXG4gICAgcmV0dXJuIHRoaXMuaGFzVXNlcihuYW1lKS50aGVuKGV4aXN0cyA9PiB7XG4gICAgICBpZiAoIWV4aXN0cykge1xuICAgICAgICBpZiAoaXNQcmVkaWNhdGUpIHtcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG51bGwpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbGV0IGVycm9yID0gbmV3IENoYXRTZXJ2aWNlRXJyb3IoJ25vVXNlcicsIG5hbWUpXG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHVzZXIpXG4gICAgfSlcbiAgfVxuXG4gIGFkZFVzZXIgKG5hbWUsIHN0YXRlKSB7XG4gICAgbGV0IHVzZXIgPSBuZXcgVXNlcih0aGlzLnNlcnZlciwgbmFtZSlcbiAgICByZXR1cm4gdXNlci5pbml0U3RhdGUoc3RhdGUpLnJldHVybih1c2VyKVxuICB9XG5cbiAgcmVtb3ZlVXNlciAobmFtZSkge1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKVxuICB9XG5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBSZWRpc1N0YXRlXG4iXX0= |
\ | No newline at end of file |