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 events = require('events');
22var caps = require('./caps');
23var data = require('./data');
24var type = require('../../core').type;
25var log = require('../../core').log;
28 * Global channel for all graphic updates
29 * capabilities exchange and input handles
31function Global(transport, fastPathTransport) {
32 this.transport = transport;
33 this.fastPathTransport = fastPathTransport;
34 // must be init via connect event
36 this.serverCapabilities = [];
37 this.clientCapabilities = [];
41inherits(Global, events.EventEmitter);
44 * Send formated PDU message
45 * @param message {type.Component} PDU message
47Global.prototype.sendPDU = function(message) {
48 this.transport.send(data.pdu(this.userId, message));
52 * Send formated Data PDU message
53 * @param message {type.Component} PDU message
55Global.prototype.sendDataPDU = function(message) {
56 this.sendPDU(data.dataPDU(message, this.shareId));
60 * Client side of Global channel automata
63function Client(transport, fastPathTransport) {
64 Global.call(this, transport, fastPathTransport);
66 this.transport.once('connect', function(core, userId, channelId) {
67 self.connect(core, userId, channelId);
68 }).on('close', function() {
70 }).on('error', function (err) {
71 self.emit('error', err);
74 if (this.fastPathTransport) {
75 this.fastPathTransport.on('fastPathData', function (secFlag, s) {
76 self.recvFastPath(secFlag, s);
80 // init client capabilities
81 this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL] = caps.generalCapability();
82 this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP] = caps.bitmapCapability();
83 this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER] = caps.orderCapability(
85 new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
86 new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
87 new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
88 new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0)
90 this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAPCACHE] = caps.bitmapCacheCapability();
91 this.clientCapabilities[caps.CapsType.CAPSTYPE_POINTER] = caps.pointerCapability();
92 this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT] = caps.inputCapability();
93 this.clientCapabilities[caps.CapsType.CAPSTYPE_BRUSH] = caps.brushCapability();
94 this.clientCapabilities[caps.CapsType.CAPSTYPE_GLYPHCACHE] = caps.glyphCapability(
96 caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(),
97 caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry()
99 this.clientCapabilities[caps.CapsType.CAPSTYPE_OFFSCREENCACHE] = caps.offscreenBitmapCacheCapability();
100 this.clientCapabilities[caps.CapsType.CAPSTYPE_VIRTUALCHANNEL] = caps.virtualChannelCapability();
101 this.clientCapabilities[caps.CapsType.CAPSTYPE_SOUND] = caps.soundCapability();
102 this.clientCapabilities[caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate();
106inherits(Client, Global);
110 * @param gccCore {type.Component(clientCoreData)}
112Client.prototype.connect = function(gccCore, userId, channelId) {
113 this.gccCore = gccCore;
114 this.userId = userId;
115 this.channelId = channelId;
117 this.transport.once('data', function(s) {
118 self.recvDemandActivePDU(s);
125Client.prototype.close = function() {
126 this.transport.close();
130 * Receive capabilities from server
131 * @param s {type.Stream}
133Client.prototype.recvDemandActivePDU = function(s) {
134 var pdu = data.pdu().read(s);
135 if (pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DEMANDACTIVEPDU) {
136 log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
140 this.transport.once('data', function(s) {
141 self.recvDemandActivePDU(s);
147 this.shareId = pdu.obj.pduMessage.obj.shareId.value;
149 // store server capabilities
150 for(var i in pdu.obj.pduMessage.obj.capabilitySets.obj) {
151 var cap = pdu.obj.pduMessage.obj.capabilitySets.obj[i].obj.capability;
155 this.serverCapabilities[cap.obj.__TYPE__] = cap;
158 this.transport.enableSecureCheckSum = !!(this.serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj.extraFlags.value & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM);
160 this.sendConfirmActivePDU();
161 this.sendClientFinalizeSynchronizePDU();
164 this.transport.once('data', function(s) {
165 self.recvServerSynchronizePDU(s);
170 * global channel automata state
171 * @param s {type.Stream}
173Client.prototype.recvServerSynchronizePDU = function(s) {
174 var pdu = data.pdu().read(s);
175 if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
176 || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_SYNCHRONIZE) {
177 log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
180 this.transport.once('data', function(s) {
181 self.recvServerSynchronizePDU(s);
187 this.transport.once('data', function(s) {
188 self.recvServerControlCooperatePDU(s);
193 * global channel automata state
194 * @param s {type.Stream}
196Client.prototype.recvServerControlCooperatePDU = function(s) {
197 var pdu = data.pdu().read(s);
198 if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
199 || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
200 || pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_COOPERATE) {
201 log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
205 this.transport.once('data', function(s) {
206 self.recvServerControlCooperatePDU(s);
211 this.transport.once('data', function(s) {
212 self.recvServerControlGrantedPDU(s);
217 * global channel automata state
218 * @param s {type.Stream}
220Client.prototype.recvServerControlGrantedPDU = function(s) {
221 var pdu = data.pdu().read(s);
222 if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
223 || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
224 || pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_GRANTED_CONTROL) {
225 log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
229 this.transport.once('data', function(s) {
230 self.recvServerControlGrantedPDU(s);
235 this.transport.once('data', function(s) {
236 self.recvServerFontMapPDU(s);
241 * global channel automata state
242 * @param s {type.Stream}
244Client.prototype.recvServerFontMapPDU = function(s) {
245 var pdu = data.pdu().read(s);
246 if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
247 || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_FONTMAP) {
248 log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
252 this.transport.once('data', function(s) {
253 self.recvServerFontMapPDU(s);
257 this.emit('connect');
259 this.transport.on('data', function(s) {
265 * Main reveive fast path
266 * @param secFlag {integer}
267 * @param s {type.Stream}
269Client.prototype.recvFastPath = function (secFlag, s) {
270 while (s.availableLength() > 0) {
271 var pdu = data.fastPathUpdatePDU().read(s);
272 switch (pdu.obj.updateHeader.value & 0xf) {
273 case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: {
274 this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj);
277 case data.FastPathUpdateType.FASTPATH_UPDATETYPE_COLOR: {
278 this.emit('pointer', pdu.obj.updateData.obj.cursorId, pdu.obj.updateData.obj.cursorStr);
287 * global channel automata state
288 * @param s {type.Stream}
290Client.prototype.recvPDU = function(s) {
291 while (s.availableLength() > 0) {
292 var pdu = data.pdu().read(s);
293 switch(pdu.obj.shareControlHeader.obj.pduType.value) {
294 case data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
296 this.transport.removeAllListeners('data');
297 this.transport.once('data', function(s) {
298 self.recvDemandActivePDU(s);
301 case data.PDUType.PDUTYPE_DATAPDU:
302 this.readDataPDU(pdu.obj.pduMessage)
305 log.debug('ignore pdu type ' + pdu.obj.shareControlHeader.obj.pduType.value);
311 * main receive for data PDU packet
312 * @param dataPDU {data.dataPDU}
314Client.prototype.readDataPDU = function (dataPDU) {
315 switch(dataPDU.obj.shareDataHeader.obj.pduType2.value) {
316 case data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
318 case data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
319 this.transport.close();
321 case data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
322 this.emit('session');
324 case data.PDUType2.PDUTYPE2_UPDATE:
325 this.readUpdateDataPDU(dataPDU.obj.pduData)
331 * Main upadate pdu receive function
332 * @param updateDataPDU
334Client.prototype.readUpdateDataPDU = function (updateDataPDU) {
335 switch(updateDataPDU.obj.updateType.value) {
336 case data.UpdateType.UPDATETYPE_BITMAP:
337 this.emit('bitmap', updateDataPDU.obj.updateData.obj.rectangles.obj)
343 * send all client capabilities
345Client.prototype.sendConfirmActivePDU = function () {
346 var generalCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj;
347 generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS;
348 generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT;
349 generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED
350 | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
351 | caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM
352 | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED;
354 var bitmapCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].obj;
355 bitmapCapability.preferredBitsPerPixel.value = this.gccCore.highColorDepth.value;
356 bitmapCapability.desktopWidth.value = this.gccCore.desktopWidth.value;
357 bitmapCapability.desktopHeight.value = this.gccCore.desktopHeight.value;
359 var orderCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].obj;
360 orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT;
362 var inputCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].obj;
363 inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE;
364 inputCapability.keyboardLayout = this.gccCore.kbdLayout;
365 inputCapability.keyboardType = this.gccCore.keyboardType;
366 inputCapability.keyboardSubType = this.gccCore.keyboardSubType;
367 inputCapability.keyboardrFunctionKey = this.gccCore.keyboardFnKeys;
368 inputCapability.imeFileName = this.gccCore.imeFileName;
370 var capabilities = new type.Component([]);
371 for(var i in this.clientCapabilities) {
372 capabilities.obj.push(caps.capability(this.clientCapabilities[i]));
375 var confirmActivePDU = data.confirmActivePDU(capabilities, this.shareId);
377 this.sendPDU(confirmActivePDU);
381 * send synchronize PDU
383Client.prototype.sendClientFinalizeSynchronizePDU = function() {
384 this.sendDataPDU(data.synchronizeDataPDU(this.channelId));
385 this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_COOPERATE));
386 this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL));
387 this.sendDataPDU(data.fontListDataPDU());
391 * Send input event as slow path input
392 * @param inputEvents {array}
394Client.prototype.sendInputEvents = function (inputEvents) {
395 var pdu = data.clientInputEventPDU(new type.Component(inputEvents.map(function (e) {
396 return data.slowPathInputEvent(e);
399 this.sendDataPDU(pdu);