linx-simulator2/node_modules/mumble-streams/lib/data.js
2019-09-18 11:11:16 +03:00

193 lines
4.6 KiB
JavaScript

var fs = require('fs'),
protobufjs = require('protobufjs'),
util = require('util'),
Transform = require('stream').Transform;
var nameById = {
0: 'Version',
1: 'UDPTunnel',
2: 'Authenticate',
3: 'Ping',
4: 'Reject',
5: 'ServerSync',
6: 'ChannelRemove',
7: 'ChannelState',
8: 'UserRemove',
9: 'UserState',
10: 'BanList',
11: 'TextMessage',
12: 'PermissionDenied',
13: 'ACL',
14: 'QueryUsers',
15: 'CryptSetup',
16: 'ContextActionModify',
17: 'ContextAction',
18: 'UserList',
19: 'VoiceTarget',
20: 'PermissionQuery',
21: 'CodecVersion',
22: 'UserStats',
23: 'RequestBlob',
24: 'ServerConfig',
25: 'SuggestConfig'
};
var idByName = {};
for (var id in nameById) {
idByName[nameById[id]] = id;
}
// Explicitly reading with readFileSync to support brfs
var mumbleProto = fs.readFileSync(__dirname + '/Mumble.proto');
var messages = protobufjs.loadProto(mumbleProto).build('MumbleProto');
/**
* Encodes the given message.
*
* @param {string} name The name of the message.
* @param {object} payload The message to be encoded.
* @return {Buffer} The encoded message.
*/
function encode(name, payload) {
var encoded = new messages[name](payload || {}).toBuffer();
// toBuffer returns an ArrayBuffer when called in the browser
if (!Buffer.isBuffer(encoded)) {
encoded = Buffer.from(encoded);
}
return encoded;
}
/**
* A message object.
* @typedef {object} Message
* @property {string} name - Name of the message
* @property {object} [payload={}] - Payload of the message
*/
/**
* Decodes the given message.
*
* @param {number} id The id of the message.
* @param {Buffer} payload The encoded message.
* @return {object} The decoded message.
*/
function decode(id, payload) {
var name = nameById[id];
return new messages[name].decode(payload || {});
}
/**
* Transform stream for encoding {@link Message Mumble messages}.
*
* @constructor
* @constructs Encoder
*/
function Encoder() {
// Allow use without new
if (!(this instanceof Encoder)) return new Encoder();
Transform.call(this, {
writableObjectMode: true
});
}
util.inherits(Encoder, Transform);
Encoder.prototype._transform = function(chunk, encoding, callback) {
if (typeof chunk.name !== 'string') {
return callback(new TypeError('chunk.name is not a string'));
}
chunk.payload = chunk.payload || {};
// First, encode the payload
var data;
if (chunk.name == 'UDPTunnel') {
// UDPTunnel message doesn't need encoding
data = chunk.payload;
} else {
try {
// Encode the message payload
data = encode(chunk.name, chunk.payload);
} catch (e) {
callback(e);
return;
}
}
// Then create the header
var header = new Buffer(6);
header.writeUInt16BE(idByName[chunk.name], 0);
header.writeUInt32BE(data.length, 2);
callback(null, Buffer.concat([header, data]));
};
/**
* Transform stream for decoding {@link Message Mumble messages}.
*
* @constructor
* @constructs Decoder
*/
function Decoder() {
// Allow use without new
if (!(this instanceof Decoder)) return new Decoder();
Transform.call(this, {
readableObjectMode: true
});
this._buffer = new Buffer(1024);
this._bufferSize = 0;
}
util.inherits(Decoder, Transform);
Decoder.prototype._transform = function(chunk, encoding, callback) {
// Add incoming chunk to internal buffer
if (this._buffer.length - this._bufferSize < chunk.length) {
// Old buffer is too small, replace with bigger one
var oldBuffer = this._buffer;
this._buffer = new Buffer(this._bufferSize + chunk.length);
oldBuffer.copy(this._buffer, 0, 0, this._bufferSize);
}
this._bufferSize += chunk.copy(this._buffer, this._bufferSize);
// Try to decode messages while we still have enough bytes
while (this._bufferSize >= 6) {
var type = this._buffer.readUInt16BE(0);
var size = this._buffer.readUInt32BE(2);
if (this._bufferSize < 6 + size) {
break; // Not enough bytes in internal buffer for the expected payload
}
var typeName = nameById[type];
var data = this._buffer.slice(6, 6 + size);
// Decode payload
var message;
if (typeName == 'UDPTunnel') {
// UDPTunnel payload is not encoded
message = new Buffer(data);
} else {
try {
message = decode(type, data);
} catch (e) {
return callback(e);
}
}
// Shift remaining bytes to start of internal buffer
this._buffer.copy(this._buffer, 0, 6 + size, this._bufferSize);
this._bufferSize -= 6 + size;
this.push({
name: typeName,
payload: message
});
}
callback();
};
module.exports = {
Encoder: Encoder,
Decoder: Decoder,
messages: messages
};