@fibercom/routeros-api
Version:
Robust MikroTik RouterOS API client for Node.js and TypeScript
214 lines • 16.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Receiver = void 0;
const iconv = require("iconv-lite");
const debug = require("debug");
const RosException_1 = require("../RosException");
const info = debug("routeros-api:connector:receiver:info");
const error = debug("routeros-api:connector:receiver:error");
const nullBuffer = Buffer.from([0x00]);
/**
* Responsible for decoding and routing raw RouterOS API socket data
* to the appropriate tagged command listeners.
*/
class Receiver {
constructor(socket) {
this.tags = new Map();
this.dataLength = 0;
this.sentencePipe = [];
this.processingSentencePipe = false;
this.currentLine = "";
this.currentReply = "";
this.currentTag = "";
this.currentPacket = [];
this.socket = socket;
}
/**
* Register a callback for a given tag. Each command gets its own tag.
*/
read(tag, callback) {
info("Reader of %s tag is being set", tag);
this.tags.set(tag, { name: tag, callback });
}
/**
* Unregister the callback for a tag once command completes (!done).
*/
stop(tag) {
info("Not reading from %s tag anymore", tag);
this.tags.delete(tag);
}
/**
* Handle incoming socket data, decode lines based on RouterOS framing protocol.
* Convert win1252-encoded text to UTF-8.
*/
processRawData(data) {
if (this.lengthDescriptorSegment) {
data = Buffer.concat([this.lengthDescriptorSegment, data]);
this.lengthDescriptorSegment = null;
}
while (data.length > 0) {
if (this.dataLength > 0) {
if (data.length <= this.dataLength) {
this.dataLength -= data.length;
this.currentLine += iconv.decode(data, "win1252");
if (this.dataLength === 0) {
this.sentencePipe.push({
sentence: this.currentLine,
hadMore: data.length !== this.dataLength,
});
this.processSentence();
this.currentLine = "";
}
break;
}
else {
const tmpBuffer = data.slice(0, this.dataLength);
const tmpStr = iconv.decode(tmpBuffer, "win1252");
this.currentLine += tmpStr;
const line = this.currentLine;
this.currentLine = "";
data = data.slice(this.dataLength);
const [descriptor_length, length] = this.decodeLength(data);
if (descriptor_length > data.length) {
this.lengthDescriptorSegment = data;
}
this.dataLength = length;
data = data.slice(descriptor_length);
if (this.dataLength === 1 && data.equals(nullBuffer)) {
this.dataLength = 0;
data = data.slice(1);
}
this.sentencePipe.push({
sentence: line,
hadMore: data.length > 0,
});
this.processSentence();
}
}
else {
const [descriptor_length, length] = this.decodeLength(data);
this.dataLength = length;
data = data.slice(descriptor_length);
if (this.dataLength === 1 && data.equals(nullBuffer)) {
this.dataLength = 0;
data = data.slice(1);
}
}
}
}
/**
* Process each complete sentence by dispatching to the appropriate tag.
*/
processSentence() {
if (!this.processingSentencePipe) {
info("Got asked to process sentence pipe");
this.processingSentencePipe = true;
const process = () => {
if (this.sentencePipe.length > 0) {
const line = this.sentencePipe.shift();
if (!line.hadMore && this.currentReply === "!fatal") {
this.socket.emit("fatal");
return;
}
info("Processing line %s", line.sentence);
if (/^\.tag=/.test(line.sentence)) {
this.currentTag = line.sentence.substring(5);
}
else if (/^!/.test(line.sentence)) {
if (this.currentTag) {
info("Received another response, sending current data to tag %s", this.currentTag);
this.sendTagData(this.currentTag);
}
this.currentPacket.push(line.sentence);
this.currentReply = line.sentence;
}
else {
this.currentPacket.push(line.sentence);
}
if (this.sentencePipe.length === 0 && this.dataLength === 0) {
if (!line.hadMore && this.currentTag) {
info("No more sentences to process, will send data to tag %s", this.currentTag);
this.sendTagData(this.currentTag);
}
else {
info("No more sentences and no data to send");
}
this.processingSentencePipe = false;
}
else {
process();
}
}
else {
this.processingSentencePipe = false;
}
};
process();
}
}
/**
* Dispatch current packet to the registered tag callback.
*/
sendTagData(currentTag) {
const tag = this.tags.get(currentTag);
if (tag) {
info("Sending to tag %s the packet %O", tag.name, this.currentPacket);
tag.callback(this.currentPacket);
}
else {
throw new RosException_1.RosException("UNREGISTEREDTAG");
}
this.cleanUp();
}
/**
* Reset tag state so next packet is not mixed.
*/
cleanUp() {
this.currentPacket = [];
this.currentTag = null;
this.currentReply = null;
}
/**
* Decode RouterOS variable-length prefix for sentence lengths.
*
* Supports multi-byte formats depending on the highest bit set.
*
* @param {Buffer} data
* @returns {[number, number]} - [lengthDescriptorSize, sentenceLength]
*/
decodeLength(data) {
let len;
let idx = 0;
const b = data[idx++];
if (b & 128) {
if ((b & 192) === 128) {
len = ((b & 63) << 8) + data[idx++];
}
else {
if ((b & 224) === 192) {
len = ((b & 31) << 8) + data[idx++];
len = (len << 8) + data[idx++];
}
else {
if ((b & 240) === 224) {
len = ((b & 15) << 8) + data[idx++];
len = (len << 8) + data[idx++];
len = (len << 8) + data[idx++];
}
else {
len = data[idx++];
len = (len << 8) + data[idx++];
len = (len << 8) + data[idx++];
len = (len << 8) + data[idx++];
}
}
}
}
else {
len = b;
}
return [idx, len];
}
}
exports.Receiver = Receiver;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVjZWl2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29ubmVjdG9yL1JlY2VpdmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLG9DQUFvQztBQUNwQywrQkFBK0I7QUFDL0Isa0RBQStDO0FBRS9DLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFDO0FBQzNELE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFDO0FBQzdELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0FBZXZDOzs7R0FHRztBQUNILE1BQWEsUUFBUTtJQVluQixZQUFZLE1BQWM7UUFWbEIsU0FBSSxHQUErQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQzdDLGVBQVUsR0FBVyxDQUFDLENBQUM7UUFDdkIsaUJBQVksR0FBZ0IsRUFBRSxDQUFDO1FBQy9CLDJCQUFzQixHQUFZLEtBQUssQ0FBQztRQUN4QyxnQkFBVyxHQUFXLEVBQUUsQ0FBQztRQUN6QixpQkFBWSxHQUFXLEVBQUUsQ0FBQztRQUMxQixlQUFVLEdBQVcsRUFBRSxDQUFDO1FBQ3hCLGtCQUFhLEdBQWEsRUFBRSxDQUFDO1FBSW5DLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNJLElBQUksQ0FBQyxHQUFXLEVBQUUsUUFBb0M7UUFDM0QsSUFBSSxDQUFDLCtCQUErQixFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxJQUFJLENBQUMsR0FBVztRQUNyQixJQUFJLENBQUMsaUNBQWlDLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDN0MsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGNBQWMsQ0FBQyxJQUFZO1FBQ2hDLElBQUksSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7WUFDakMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUMzRCxJQUFJLENBQUMsdUJBQXVCLEdBQUcsSUFBSSxDQUFDO1FBQ3RDLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdkIsSUFBSSxJQUFJLENBQUMsVUFBVSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUN4QixJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO29CQUNuQyxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUM7b0JBQy9CLElBQUksQ0FBQyxXQUFXLElBQUksS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7b0JBRWxELElBQUksSUFBSSxDQUFDLFVBQVUsS0FBSyxDQUFDLEVBQUUsQ0FBQzt3QkFDMUIsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUM7NEJBQ3JCLFFBQVEsRUFBRSxJQUFJLENBQUMsV0FBVzs0QkFDMUIsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLFVBQVU7eUJBQ3pDLENBQUMsQ0FBQzt3QkFDSCxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7d0JBQ3ZCLElBQUksQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO29CQUN4QixDQUFDO29CQUNELE1BQU07Z0JBQ1IsQ0FBQztxQkFBTSxDQUFDO29CQUNOLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztvQkFDakQsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUM7b0JBQ2xELElBQUksQ0FBQyxXQUFXLElBQUksTUFBTSxDQUFDO29CQUUzQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO29CQUM5QixJQUFJLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQztvQkFDdEIsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO29CQUVuQyxNQUFNLENBQUMsaUJBQWlCLEVBQUUsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDNUQsSUFBSSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7d0JBQ3BDLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLENBQUM7b0JBQ3RDLENBQUM7b0JBRUQsSUFBSSxDQUFDLFVBQVUsR0FBRyxNQUFNLENBQUM7b0JBQ3pCLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUM7b0JBRXJDLElBQUksSUFBSSxDQUFDLFVBQVUsS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO3dCQUNyRCxJQUFJLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQzt3QkFDcEIsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ3ZCLENBQUM7b0JBRUQsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUM7d0JBQ3JCLFFBQVEsRUFBRSxJQUFJO3dCQUNkLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUM7cUJBQ3pCLENBQUMsQ0FBQztvQkFDSCxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3pCLENBQUM7WUFDSCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxDQUFDLGlCQUFpQixFQUFFLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzVELElBQUksQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDO2dCQUN6QixJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO2dCQUVyQyxJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssQ0FBQyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztvQkFDckQsSUFBSSxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUM7b0JBQ3BCLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN2QixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxlQUFlO1FBQ3JCLElBQUksQ0FBQyxJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztZQUNqQyxJQUFJLENBQUMsb0NBQW9DLENBQUMsQ0FBQztZQUMzQyxJQUFJLENBQUMsc0JBQXNCLEdBQUcsSUFBSSxDQUFDO1lBRW5DLE1BQU0sT0FBTyxHQUFHLEdBQUcsRUFBRTtnQkFDbkIsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDakMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxLQUFLLEVBQUUsQ0FBQztvQkFFdkMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLFlBQVksS0FBSyxRQUFRLEVBQUUsQ0FBQzt3QkFDcEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7d0JBQzFCLE9BQU87b0JBQ1QsQ0FBQztvQkFFRCxJQUFJLENBQUMsb0JBQW9CLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUUxQyxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7d0JBQ2xDLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQy9DLENBQUM7eUJBQU0sSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO3dCQUNwQyxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQzs0QkFDcEIsSUFBSSxDQUNGLDJEQUEyRCxFQUMzRCxJQUFJLENBQUMsVUFBVSxDQUNoQixDQUFDOzRCQUNGLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO3dCQUNwQyxDQUFDO3dCQUNELElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQzt3QkFDdkMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO29CQUNwQyxDQUFDO3lCQUFNLENBQUM7d0JBQ04sSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUN6QyxDQUFDO29CQUVELElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssQ0FBQyxFQUFFLENBQUM7d0JBQzVELElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQzs0QkFDckMsSUFBSSxDQUNGLHdEQUF3RCxFQUN4RCxJQUFJLENBQUMsVUFBVSxDQUNoQixDQUFDOzRCQUNGLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO3dCQUNwQyxDQUFDOzZCQUFNLENBQUM7NEJBQ04sSUFBSSxDQUFDLHVDQUF1QyxDQUFDLENBQUM7d0JBQ2hELENBQUM7d0JBQ0QsSUFBSSxDQUFDLHNCQUFzQixHQUFHLEtBQUssQ0FBQztvQkFDdEMsQ0FBQzt5QkFBTSxDQUFDO3dCQUNOLE9BQU8sRUFBRSxDQUFDO29CQUNaLENBQUM7Z0JBQ0gsQ0FBQztxQkFBTSxDQUFDO29CQUNOLElBQUksQ0FBQyxzQkFBc0IsR0FBRyxLQUFLLENBQUM7Z0JBQ3RDLENBQUM7WUFDSCxDQUFDLENBQUM7WUFFRixPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxXQUFXLENBQUMsVUFBa0I7UUFDcEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDdEMsSUFBSSxHQUFHLEVBQUUsQ0FBQztZQUNSLElBQUksQ0FBQyxpQ0FBaUMsRUFBRSxHQUFHLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUN0RSxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUNuQyxDQUFDO2FBQU0sQ0FBQztZQUNOLE1BQU0sSUFBSSwyQkFBWSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDNUMsQ0FBQztRQUNELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNqQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxPQUFPO1FBQ2IsSUFBSSxDQUFDLGFBQWEsR0FBRyxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDdkIsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUM7SUFDM0IsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSyxZQUFZLENBQUMsSUFBWTtRQUMvQixJQUFJLEdBQUcsQ0FBQztRQUNSLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO1FBRXRCLElBQUksQ0FBQyxHQUFHLEdBQUcsRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQztnQkFDdEIsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7WUFDdEMsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLElBQUksQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7b0JBQ3RCLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO29CQUNwQyxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7Z0JBQ2pDLENBQUM7cUJBQU0sQ0FBQztvQkFDTixJQUFJLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDO3dCQUN0QixHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQzt3QkFDcEMsR0FBRyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO3dCQUMvQixHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7b0JBQ2pDLENBQUM7eUJBQU0sQ0FBQzt3QkFDTixHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7d0JBQ2xCLEdBQUcsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQzt3QkFDL0IsR0FBRyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO3dCQUMvQixHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7b0JBQ2pDLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO2FBQU0sQ0FBQztZQUNOLEdBQUcsR0FBRyxDQUFDLENBQUM7UUFDVixDQUFDO1FBRUQsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNwQixDQUFDO0NBQ0Y7QUExTkQsNEJBME5DIn0=