2 * Copyright (c) 2014-2015 Sylvain Peyrefitte
4 * This file is part of node-rdpjs.
6 * node-rdpjs is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20var inherits = require('util').inherits;
21var log = require('./log');
22var error = require('./error');
25 * Stream wrapper around buffer type
26 * @param i {Buffer | integer} size of init buffer
31 if (i instanceof Buffer) {
35 this.buffer = Buffer.alloc(i || 8192);
40 * Return length of available data in stream
41 * @returns {Number} length of available data in stream
43Stream.prototype.availableLength = function() {
44 return this.buffer.length - this.offset;
49 * @param length {integer} length of padding
51Stream.prototype.readPadding = function(length) {
52 this.offset += length;
56 * Format string buffer
57 * @returns {string} buffer stringified
59Stream.prototype.getValue = function() {
64 * @param value {object | function} inner value
67function CallableValue(value) {
74 * For syntaxic purpose
76Object.defineProperty(CallableValue.prototype, "value", {
77 get: function() { return this._value(); },
79 if(typeof e !== 'function') {
80 this._value = function () { return e; };
89 * Type readable or writable by binary stream
91 * .conditional {boolean} read or write type depend on conditional call
95 CallableValue.call(this);
97 this.isReaded = false;
98 this.isWritten = false;
101inherits(Type, CallableValue);
104 * Write type into binary stream s
105 * @param {type.Stream} s binary stream
107Type.prototype.write = function(s) {
108 //do not write false conditional type
109 if(this.opt.conditional && !this.opt.conditional())
112 this.isWritten = true;
119 * Read type from binary stream
120 * @param {type.Stream} s binary stream
121 * @returns this to chain call
123Type.prototype.read = function(s) {
124 //do not read false conditional type
125 if(this.opt.conditional && !this.opt.conditional())
128 if(this.opt.optional && s.availableLength() < this.size())
131 this.isReaded = true;
134 if(this.opt.constant) {
135 var oldValue = this.value;
140 if (e instanceof RangeError) {
141 throw new error.ProtocolError("NODE_RDP_CORE_TYPE_STREAM_TOO_SMALL");
146 if(oldValue !== this.value) {
147 log.error('constant value mismatch ' + oldValue + ' != ' + this.value);
148 throw new error.ProtocolError("NODE_RDP_CORE_TYPE_CONSTANT_VALUE_MISMATCH, OLD:" + oldValue + ", NEW:" + this.value);
156 if (e instanceof RangeError) {
157 throw new error.ProtocolError("NODE_RDP_CORE_TYPE_STREAM_TOO_SMALL");
168 * @returns {int} Size of type
170Type.prototype.size = function() {
171 if(this.opt.conditional && !this.opt.conditional())
173 return this._size_();
177 * Convert type to stream
178 * Usefull when you want to buffer
181Type.prototype.toStream = function() {
182 var result = new Stream(this.size());
189 * @param {object} obj composite object
190 * @param {object} opt Type parameters
192function Component(obj, opt) {
193 Type.call(this, opt);
198inherits(Component, Type);
202 * @param i {string} index name in obj
203 * @returns {Boolean} true if can be ignore
205Component.prototype.ignore = function(i) {
206 // ignore meta information
207 if(i.lastIndexOf("__", 0) === 0) {
211 if(typeof(this.obj[i]) === 'function') {
218 * Write each sub type into stream
221Component.prototype.writeValue = function(s) {
222 for(var i in this.obj) {
227 this.obj[i].write(s);
230 log.info('during write of field ' + i);
237 * Read each sub type into stream
238 * @param {Stream} s from read stream
240Component.prototype.readValue = function(s) {
242 if(this.opt.readLength) {
243 readStream = new Stream(s.buffer.slice(s.offset, s.offset + this.opt.readLength.value));
246 for(var i in this.obj) {
247 // ignore meta information
252 this.obj[i].read(readStream);
255 log.info('during read of field ' + i);
261 if (this.opt.readLength) {
262 s.offset += this.opt.readLength.value;
263 if (readStream.offset < this.opt.readLength.value) {
264 log.debug('still have available data : read it as padding');
270 * Sum size of sub types
272Component.prototype._size_ = function() {
274 for(var i in this.obj) {
278 size += this.obj[i].size();
285 * @param {number} value of type
286 * @param {function} readBufferCallback Buffer prototype read function
287 * @param {function} writeBufferCallback Buffer prototype write function
288 * @param {object} opt Type parameter
290function SingleType(value, nbBytes, readBufferCallback, writeBufferCallback, opt){
291 Type.call(this, opt);
292 this.value = value || 0;
293 this.nbBytes = nbBytes;
294 this.readBufferCallback = readBufferCallback;
295 this.writeBufferCallback = writeBufferCallback;
299inherits(SingleType, Type);
302 * Write SingleType value into stream
305SingleType.prototype.writeValue = function(s) {
306 var safeValue = this.value;
307 if (typeof safeValue === 'number') { // FIX: Sanitize coordinates to prevent crashes
308 safeValue = Math.round(safeValue); // Round to nearest integer (fixes -1.01 issues)
309 if (safeValue < 0) safeValue = 0; // Clamp to 0 if negative
310 // nbBytes is 1 (UInt8), 2 (UInt16), or 4 (UInt32)
311 var max = Math.pow(2, this.nbBytes * 8) - 1;
312 if (safeValue > max) safeValue = max; // Clamp to max value allowed by the buffer size (prevents overflow crashes)
314 this.writeBufferCallback.call(s.buffer, safeValue, s.offset);
315 s.offset += this._size_();
319 * Read SingleType value into stream
320 * @param {Stream} s from read stream
322SingleType.prototype.readValue = function(s) {
323 this.value = this.readBufferCallback.call(s.buffer, s.offset);
324 s.offset += this._size_();
328 * Size of single type
329 * @returns Size of single type
331SingleType.prototype._size_ = function() {
337 * @param {number | function} value of type
338 * @param {object} opt Type parameter
341function UInt8(value, opt) {
342 SingleType.call(this, value, 1, Buffer.prototype.readUInt8, Buffer.prototype.writeUInt8, opt);
346inherits(UInt8, SingleType);
349 * Integer on 2 bytes in Little Endian
350 * @param {number | function} value to write or compare if constant
351 * @param {object} opt Type parameter
354function UInt16Le(value, opt) {
355 SingleType.call(this, value, 2, Buffer.prototype.readUInt16LE, Buffer.prototype.writeUInt16LE, opt);
359inherits(UInt16Le, SingleType);
362 * Integer on 2 bytes in Big Endian
363 * @param {number | function} value to write or compare if constant
364 * @param {object} opt Type parameter
367function UInt16Be(value, opt) {
368 SingleType.call(this, value, 2, Buffer.prototype.readUInt16BE, Buffer.prototype.writeUInt16BE, opt);
372inherits(UInt16Be, SingleType);
375 * Integer on 4 bytes in Little Endian
376 * @param {number | function} value to write or compare if constant
377 * @param {object} opt Type parameter
380function UInt32Le(value, opt) {
381 SingleType.call(this, value, 4, Buffer.prototype.readUInt32LE, Buffer.prototype.writeUInt32LE, opt);
385inherits(UInt32Le, SingleType);
388 * Integer on 4 bytes in Big Endian
389 * @param {number | function} value to write or compare if constant
390 * @param {object} opt Type parameter
393function UInt32Be(value, opt) {
394 SingleType.call(this, value, 4, Buffer.prototype.readUInt32BE, Buffer.prototype.writeUInt32BE, opt);
398inherits(UInt32Be, SingleType);
401 * @param value {Buffer} javascript source string
402 * @param opt {object} type options
403 * .readLength {type} length for reading operation
404 * @returns {type.BinaryString}
406function BinaryString(value, opt) {
407 Type.call(this, opt);
408 this.value = value || Buffer.alloc(0);
412inherits(BinaryString, Type);
415 * Write value into string
416 * @param s {type.Stream}
418BinaryString.prototype.writeValue = function(s) {
419 this.value.copy(s.buffer, s.offset);
420 s.offset += this._size_();
424 * Read string from offset to read length if specified or end of stream
425 * @param s {type.Stream}
427BinaryString.prototype.readValue = function(s) {
428 if(this.opt.readLength) {
429 this.value = s.buffer.slice(s.offset, s.offset + this.opt.readLength.value);
432 this.value = s.buffer.slice(s.offset);
434 s.offset += this._size_();
438 * @returns {integer} length of string
440BinaryString.prototype._size_ = function() {
441 return this.value.length;
445 * Dynamic built type depend on factory function
446 * @param message {object} parent object
447 * @param field {string} name of object field
448 * @param factory {function} factory use to built new type
449 * @param opt {object} type options
451function Factory(factory, opt) {
452 Type.call(this, opt);
453 this.factory = factory;
457inherits(Factory, Type);
460 * build type and write into stream
461 * @param s {Stream} input stream
463Factory.prototype.writeValue = function(s) {
468 * build type and read from stream
469 * @param s {Stream} input stream
471Factory.prototype.readValue = function(s) {
476 * must be never called
478Factory.prototype._size_ = function() {
479 throw new error.FatalError('NODE_RDP_CORE_TYPE_FACTORY_TYPE_HAVE_NO_SIZE');
487 Component : Component,
493 BinaryString : BinaryString,
494 CallableValue : CallableValue,