master
js 407 lines 13.4 KB
Raw
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
20 var inherits = require('util').inherits;
21 var events = require('events');
22 var caps = require('./caps');
23 var data = require('./data');
24 var type = require('../../core').type;
25 var log = require('../../core').log;
26
27 /**
28 * Global channel for all graphic updates
29 * capabilities exchange and input handles
30 */
31 function 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
41 inherits(Global, events.EventEmitter);
42
43 /**
44 * Send formatted PDU message
45 * @param message {type.Component} PDU message
46 */
47 Global.prototype.sendPDU = function(message) {
48 this.transport.send(data.pdu(this.userId, message));
49 };
50
51 /**
52 * Send formatted Data PDU message
53 * @param message {type.Component} PDU message
54 */
55 Global.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 */
63 function 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
106 inherits(Client, Global);
107
108 /**
109 * connect function
110 * @param gccCore {type.Component(clientCoreData)}
111 */
112 Client.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 */
125 Client.prototype.close = function() {
126 this.transport.close();
127 };
128
129 /**
130 * Receive capabilities from server
131 * @param s {type.Stream}
132 */
133 Client.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 */
173 Client.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 */
196 Client.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 */
220 Client.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 */
244 Client.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 */
269 Client.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 */
290 Client.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 */
314 Client.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 */
334 Client.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 */
345 Client.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 */
383 Client.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 */
394 Client.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 */
405 module.exports = {
406 Client : Client
407 };