EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
global.js
Go to the documentation of this file.
1/*
2 * Copyright (c) 2014-2015 Sylvain Peyrefitte
3 *
4 * This file is part of node-rdpjs.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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;
26
27/**
28 * Global channel for all graphic updates
29 * capabilities exchange and input handles
30 */
31function Global(transport, fastPathTransport) {
32 this.transport = transport;
33 this.fastPathTransport = fastPathTransport;
34 // must be init via connect event
35 this.userId = 0;
36 this.serverCapabilities = [];
37 this.clientCapabilities = [];
38}
39
40//inherit from Layer
41inherits(Global, events.EventEmitter);
42
43/**
44 * Send formated PDU message
45 * @param message {type.Component} PDU message
46 */
47Global.prototype.sendPDU = function(message) {
48 this.transport.send(data.pdu(this.userId, message));
49};
50
51/**
52 * Send formated Data PDU message
53 * @param message {type.Component} PDU message
54 */
55Global.prototype.sendDataPDU = function(message) {
56 this.sendPDU(data.dataPDU(message, this.shareId));
57};
58
59/**
60 * Client side of Global channel automata
61 * @param transport
62 */
63function Client(transport, fastPathTransport) {
64 Global.call(this, transport, fastPathTransport);
65 var self = this;
66 this.transport.once('connect', function(core, userId, channelId) {
67 self.connect(core, userId, channelId);
68 }).on('close', function() {
69 self.emit('close');
70 }).on('error', function (err) {
71 self.emit('error', err);
72 });
73
74 if (this.fastPathTransport) {
75 this.fastPathTransport.on('fastPathData', function (secFlag, s) {
76 self.recvFastPath(secFlag, s);
77 });
78 }
79
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(
84 new type.Component([
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)
89 ]));
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(
95 new type.Component([
96 caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(),
97 caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry()
98 ]));
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();
103}
104
105// inherit from Layer
106inherits(Client, Global);
107
108/**
109 * connect function
110 * @param gccCore {type.Component(clientCoreData)}
111 */
112Client.prototype.connect = function(gccCore, userId, channelId) {
113 this.gccCore = gccCore;
114 this.userId = userId;
115 this.channelId = channelId;
116 var self = this;
117 this.transport.once('data', function(s) {
118 self.recvDemandActivePDU(s);
119 });
120};
121
122/**
123 * close stack
124 */
125Client.prototype.close = function() {
126 this.transport.close();
127};
128
129/**
130 * Receive capabilities from server
131 * @param s {type.Stream}
132 */
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');
137
138 // loop on state
139 var self = this;
140 this.transport.once('data', function(s) {
141 self.recvDemandActivePDU(s);
142 });
143 return;
144 }
145
146 // store share id
147 this.shareId = pdu.obj.pduMessage.obj.shareId.value;
148
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;
152 if(!cap.obj) {
153 continue;
154 }
155 this.serverCapabilities[cap.obj.__TYPE__] = cap;
156 }
157
158 this.transport.enableSecureCheckSum = !!(this.serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj.extraFlags.value & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM);
159
160 this.sendConfirmActivePDU();
161 this.sendClientFinalizeSynchronizePDU();
162
163 var self = this;
164 this.transport.once('data', function(s) {
165 self.recvServerSynchronizePDU(s);
166 });
167};
168
169/**
170 * global channel automata state
171 * @param s {type.Stream}
172 */
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');
178 // loop on state
179 var self = this;
180 this.transport.once('data', function(s) {
181 self.recvServerSynchronizePDU(s);
182 });
183 return;
184 }
185
186 var self = this;
187 this.transport.once('data', function(s) {
188 self.recvServerControlCooperatePDU(s);
189 });
190};
191
192/**
193 * global channel automata state
194 * @param s {type.Stream}
195 */
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');
202
203 // loop on state
204 var self = this;
205 this.transport.once('data', function(s) {
206 self.recvServerControlCooperatePDU(s);
207 });
208 }
209
210 var self = this;
211 this.transport.once('data', function(s) {
212 self.recvServerControlGrantedPDU(s);
213 });
214};
215
216/**
217 * global channel automata state
218 * @param s {type.Stream}
219 */
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');
226
227 // loop on state
228 var self = this;
229 this.transport.once('data', function(s) {
230 self.recvServerControlGrantedPDU(s);
231 });
232 }
233
234 var self = this;
235 this.transport.once('data', function(s) {
236 self.recvServerFontMapPDU(s);
237 });
238};
239
240/**
241 * global channel automata state
242 * @param s {type.Stream}
243 */
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');
249
250 // loop on state
251 var self = this;
252 this.transport.once('data', function(s) {
253 self.recvServerFontMapPDU(s);
254 });
255 }
256
257 this.emit('connect');
258 var self = this;
259 this.transport.on('data', function(s) {
260 self.recvPDU(s);
261 });
262};
263
264/**
265 * Main reveive fast path
266 * @param secFlag {integer}
267 * @param s {type.Stream}
268 */
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);
275 break;
276 }
277 case data.FastPathUpdateType.FASTPATH_UPDATETYPE_COLOR: {
278 this.emit('pointer', pdu.obj.updateData.obj.cursorId, pdu.obj.updateData.obj.cursorStr);
279 break;
280 }
281 default:
282 }
283 }
284};
285
286/**
287 * global channel automata state
288 * @param s {type.Stream}
289 */
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:
295 var self = this;
296 this.transport.removeAllListeners('data');
297 this.transport.once('data', function(s) {
298 self.recvDemandActivePDU(s);
299 });
300 break;
301 case data.PDUType.PDUTYPE_DATAPDU:
302 this.readDataPDU(pdu.obj.pduMessage)
303 break;
304 default:
305 log.debug('ignore pdu type ' + pdu.obj.shareControlHeader.obj.pduType.value);
306 }
307 }
308};
309
310/**
311 * main receive for data PDU packet
312 * @param dataPDU {data.dataPDU}
313 */
314Client.prototype.readDataPDU = function (dataPDU) {
315 switch(dataPDU.obj.shareDataHeader.obj.pduType2.value) {
316 case data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
317 break;
318 case data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
319 this.transport.close();
320 break;
321 case data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
322 this.emit('session');
323 break;
324 case data.PDUType2.PDUTYPE2_UPDATE:
325 this.readUpdateDataPDU(dataPDU.obj.pduData)
326 break;
327 }
328};
329
330/**
331 * Main upadate pdu receive function
332 * @param updateDataPDU
333 */
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)
338 break;
339 }
340};
341
342/**
343 * send all client capabilities
344 */
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;
353
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;
358
359 var orderCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].obj;
360 orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT;
361
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;
369
370 var capabilities = new type.Component([]);
371 for(var i in this.clientCapabilities) {
372 capabilities.obj.push(caps.capability(this.clientCapabilities[i]));
373 }
374
375 var confirmActivePDU = data.confirmActivePDU(capabilities, this.shareId);
376
377 this.sendPDU(confirmActivePDU);
378};
379
380/**
381 * send synchronize PDU
382 */
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());
388};
389
390/**
391 * Send input event as slow path input
392 * @param inputEvents {array}
393 */
394Client.prototype.sendInputEvents = function (inputEvents) {
395 var pdu = data.clientInputEventPDU(new type.Component(inputEvents.map(function (e) {
396 return data.slowPathInputEvent(e);
397 })));
398
399 this.sendDataPDU(pdu);
400};
401
402/**
403 * Module exports
404 */
405module.exports = {
406 Client : Client
407};