First pass at adding RDP clipboard support, #3810.
Ylian Saint-Hilaire committed
May 14, 2022 at 23:00 UTC
af052ddfe7bfde2c8d1286831a27c2e495566bc0
10 files changed
+446
-21
MeshCentralServer.njsproj
+1
@@ -198,6 +198,7 @@
198
<Compile Include="rdp\protocol\index.js" />
199
<Compile Include="rdp\protocol\nla.js" />
200
<Compile Include="rdp\protocol\pdu\caps.js" />
201
+ <Compile Include="rdp\protocol\pdu\cliprdr.js" />
202
<Compile Include="rdp\protocol\pdu\data.js" />
203
<Compile Include="rdp\protocol\pdu\global.js" />
204
<Compile Include="rdp\protocol\pdu\index.js" />
apprelays.js
+6
-1
@@ -152,7 +152,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
152
obj.wsClient._socket.pause();
153
try {
154
obj.relaySocket.write(data, function () {
155
- try { obj.wsClient._socket.resume(); } catch (ex) { console.log(ex); }
155
+ if (obj.wsClient && obj.wsClient._socket) { try { obj.wsClient._socket.resume(); } catch (ex) { console.log(ex); } }
156
});
157
} catch (ex) { console.log(ex); obj.close(); }
158
}
@@ -201,6 +201,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
201
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
202
delete bitmap.data;
203
send(['rdp-bitmap', bitmap]); // Send the bitmap metadata seperately, without bitmap data.
204
+ }).on('clipboard', function (content) {
205
+ // Clipboard data changed
206
+ console.log('RDP clipboard recv', content);
207
+ send(['rdp-clipboard', content]);
208
}).on('close', function () {
209
send(['rdp-close']);
210
}).on('error', function (err) {
@@ -317,6 +321,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
321
}
322
case 'mouse': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendPointerEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
323
case 'wheel': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendWheelEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
324
+ case 'clipboard': { rdpClient.setClipboardData(msg[1]); break; }
325
case 'scancode': {
326
if (obj.limitedinput == true) { // Limit keyboard input
327
var ok = false, k = msg[1];
public/scripts/agent-rdp-0.0.1.js
+15
-3
@@ -83,6 +83,10 @@ var CreateRDPDesktop = function (canvasid) {
83
obj.Stop();
84
break;
85
}
86
+ case 'rdp-clipboard': {
87
+ console.log('clipboard', msg[1]);
88
+ break;
89
+ }
90
case 'ping': { obj.socket.send('["pong"]'); break; }
91
case 'pong': { break; }
92
}
@@ -99,7 +103,15 @@ var CreateRDPDesktop = function (canvasid) {
103
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
104
if (obj.socket) { obj.socket.close(); }
105
}
102
-
106
+
107
+ obj.m.setClipboard = function (content) {
108
+ console.log('s1');
109
+ if (obj.socket) {
110
+ console.log('s2', content);
111
+ obj.socket.send(JSON.stringify(['clipboard', content]));
112
+ }
113
+ }
114
+
115
function changeState(newstate) {
116
if (obj.State == newstate) return;
117
obj.State = newstate;
@@ -153,14 +165,14 @@ var CreateRDPDesktop = function (canvasid) {
165
}
166
obj.m.handleKeyUp = function (e) {
167
if (!obj.socket || (obj.State != 3)) return;
156
- console.log('handleKeyUp', Mstsc.scancode(e));
168
+ //console.log('handleKeyUp', Mstsc.scancode(e));
169
obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), false]));
170
e.preventDefault();
171
return false;
172
}
173
obj.m.handleKeyDown = function (e) {
174
if (!obj.socket || (obj.State != 3)) return;
163
- console.log('handleKeyDown', Mstsc.scancode(e));
175
+ //console.log('handleKeyDown', Mstsc.scancode(e));
176
obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), true]));
177
e.preventDefault();
178
return false;
rdp/protocol/pdu/cliprdr.js
new
+327
@@ -0,0 +1,327 @@
1
+const type = require('../../core').type;
2
+const EventEmitter = require('events').EventEmitter;
3
+const caps = require('./caps');
4
+const log = require('../../core').log;
5
+const data = require('./data');
6
+
7
+
8
+
9
+/**
10
+ * Cliprdr channel for all clipboard
11
+ * capabilities exchange
12
+ */
13
+class Cliprdr extends EventEmitter {
14
+
15
+ constructor(transport) {
16
+ super();
17
+ this.transport = transport;
18
+ // must be init via connect event
19
+ this.userId = 0;
20
+ this.serverCapabilities = [];
21
+ this.clientCapabilities = [];
22
+ }
23
+
24
+}
25
+
26
+
27
+/**
28
+ * Client side of Cliprdr channel automata
29
+ * @param transport
30
+ */
31
+class Client extends Cliprdr {
32
+
33
+ constructor(transport, fastPathTransport) {
34
+
35
+ super(transport, fastPathTransport);
36
+
37
+ this.transport.once('connect', (gccCore, userId, channelId) => {
38
+ this.connect(gccCore, userId, channelId);
39
+ }).on('close', () => {
40
+ this.emit('close');
41
+ }).on('error', (err) => {
42
+ this.emit('error', err);
43
+ });
44
+
45
+ this.content = '';
46
+
47
+ }
48
+
49
+ /**
50
+ * connect function
51
+ * @param gccCore {type.Component(clientCoreData)}
52
+ */
53
+ connect(gccCore, userId, channelId) {
54
+ this.gccCore = gccCore;
55
+ this.userId = userId;
56
+ this.channelId = channelId;
57
+ this.transport.once('cliprdr', (s) => {
58
+ this.recv(s);
59
+ });
60
+ }
61
+
62
+
63
+ send(message) {
64
+ this.transport.send('cliprdr', new type.Component([
65
+ // Channel PDU Header
66
+ new type.UInt32Le(message.size()),
67
+ // CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST | CHANNEL_FLAG_SHOW_PROTOCOL
68
+ new type.UInt32Le(0x13),
69
+ message
70
+ ]));
71
+ };
72
+
73
+ recv(s) {
74
+ s.offset = 18;
75
+ const pdu = data.clipPDU().read(s), type = data.ClipPDUMsgType;
76
+
77
+ switch (pdu.obj.header.obj.msgType.value) {
78
+ case type.CB_MONITOR_READY:
79
+ this.recvMonitorReadyPDU(s);
80
+ break;
81
+ case type.CB_FORMAT_LIST:
82
+ this.recvFormatListPDU(s);
83
+ break;
84
+ case type.CB_FORMAT_LIST_RESPONSE:
85
+ this.recvFormatListResponsePDU(s);
86
+ break;
87
+ case type.CB_FORMAT_DATA_REQUEST:
88
+ this.recvFormatDataRequestPDU(s);
89
+ break;
90
+ case type.CB_FORMAT_DATA_RESPONSE:
91
+ this.recvFormatDataResponsePDU(s);
92
+ break;
93
+ case type.CB_TEMP_DIRECTORY:
94
+ break;
95
+ case type.CB_CLIP_CAPS:
96
+ this.recvClipboardCapsPDU(s);
97
+ break;
98
+ case type.CB_FILECONTENTS_REQUEST:
99
+ }
100
+
101
+ this.transport.once('cliprdr', (s) => {
102
+ this.recv(s);
103
+ });
104
+ }
105
+
106
+ /**
107
+ * Receive capabilities from server
108
+ * @param s {type.Stream}
109
+ */
110
+ recvClipboardCapsPDU(s) {
111
+ // Start at 18
112
+ s.offset = 18;
113
+ // const pdu = data.clipPDU().read(s);
114
+ // console.log('recvClipboardCapsPDU', s);
115
+ }
116
+
117
+
118
+ /**
119
+ * Receive monitor ready from server
120
+ * @param s {type.Stream}
121
+ */
122
+ recvMonitorReadyPDU(s) {
123
+ s.offset = 18;
124
+ // const pdu = data.clipPDU().read(s);
125
+ // console.log('recvMonitorReadyPDU', s);
126
+
127
+ this.sendClipboardCapsPDU();
128
+ // this.sendClientTemporaryDirectoryPDU();
129
+ this.sendFormatListPDU();
130
+ }
131
+
132
+
133
+ /**
134
+ * Send clipboard capabilities PDU
135
+ */
136
+ sendClipboardCapsPDU() {
137
+ this.send(new type.Component({
138
+ msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_CLIP_CAPS),
139
+ msgFlags: new type.UInt16Le(0x00),
140
+ dataLen: new type.UInt32Le(0x10),
141
+ cCapabilitiesSets: new type.UInt16Le(0x01),
142
+ pad1: new type.UInt16Le(0x00),
143
+ capabilitySetType: new type.UInt16Le(0x01),
144
+ lengthCapability: new type.UInt16Le(0x0c),
145
+ version: new type.UInt32Le(0x02),
146
+ capabilityFlags: new type.UInt32Le(0x02)
147
+ }));
148
+ }
149
+
150
+
151
+ /**
152
+ * Send client temporary directory PDU
153
+ */
154
+ sendClientTemporaryDirectoryPDU(path = '') {
155
+ // TODO
156
+ this.send(new type.Component({
157
+ msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_TEMP_DIRECTORY),
158
+ msgFlags: new type.UInt16Le(0x00),
159
+ dataLen: new type.UInt32Le(0x0208),
160
+ wszTempDir: new type.BinaryString(Buffer.from('D:\\Vectors' + Array(251).join('\x00'), 'ucs2'), { readLength : new type.CallableValue(520)})
161
+ }));
162
+ }
163
+
164
+
165
+ /**
166
+ * Send format list PDU
167
+ */
168
+ sendFormatListPDU() {
169
+ this.send(new type.Component({
170
+ msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_LIST),
171
+ msgFlags: new type.UInt16Le(0x00),
172
+
173
+ dataLen: new type.UInt32Le(0x24),
174
+
175
+ formatId6: new type.UInt32Le(0xc004),
176
+ formatName6: new type.BinaryString(Buffer.from('Native\x00' , 'ucs2'), { readLength : new type.CallableValue(14)}),
177
+
178
+ formatId8: new type.UInt32Le(0x0d),
179
+ formatName8: new type.UInt16Le(0x00),
180
+
181
+ formatId9: new type.UInt32Le(0x10),
182
+ formatName9: new type.UInt16Le(0x00),
183
+
184
+ formatId0: new type.UInt32Le(0x01),
185
+ formatName0: new type.UInt16Le(0x00),
186
+
187
+ // dataLen: new type.UInt32Le(0xe0),
188
+
189
+ // formatId1: new type.UInt32Le(0xc08a),
190
+ // formatName1: new type.BinaryString(Buffer.from('Rich Text Format\x00' , 'ucs2'), { readLength : new type.CallableValue(34)}),
191
+
192
+ // formatId2: new type.UInt32Le(0xc145),
193
+ // formatName2: new type.BinaryString(Buffer.from('Rich Text Format Without Objects\x00' , 'ucs2'), { readLength : new type.CallableValue(66)}),
194
+
195
+ // formatId3: new type.UInt32Le(0xc143),
196
+ // formatName3: new type.BinaryString(Buffer.from('RTF As Text\x00' , 'ucs2'), { readLength : new type.CallableValue(24)}),
197
+
198
+ // formatId4: new type.UInt32Le(0x01),
199
+ // formatName4: new type.BinaryString(0x00),
200
+
201
+ formatId5: new type.UInt32Le(0x07),
202
+ formatName5: new type.UInt16Le(0x00),
203
+
204
+ // formatId6: new type.UInt32Le(0xc004),
205
+ // formatName6: new type.BinaryString(Buffer.from('Native\x00' , 'ucs2'), { readLength : new type.CallableValue(14)}),
206
+
207
+ // formatId7: new type.UInt32Le(0xc00e),
208
+ // formatName7: new type.BinaryString(Buffer.from('Object Descriptor\x00' , 'ucs2'), { readLength : new type.CallableValue(36)}),
209
+
210
+ // formatId8: new type.UInt32Le(0x03),
211
+ // formatName8: new type.UInt16Le(0x00),
212
+
213
+ // formatId9: new type.UInt32Le(0x10),
214
+ // formatName9: new type.UInt16Le(0x00),
215
+
216
+ // formatId0: new type.UInt32Le(0x07),
217
+ // formatName0: new type.UInt16Le(0x00),
218
+ }));
219
+
220
+ }
221
+
222
+ /**
223
+ * Recvie format list PDU from server
224
+ * @param {type.Stream} s
225
+ */
226
+ recvFormatListPDU(s) {
227
+ s.offset = 18;
228
+ // const pdu = data.clipPDU().read(s);
229
+ // console.log('recvFormatListPDU', s);
230
+ this.sendFormatListResponsePDU();
231
+ }
232
+
233
+
234
+ /**
235
+ * Send format list reesponse
236
+ */
237
+ sendFormatListResponsePDU() {
238
+ this.send(new type.Component({
239
+ msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_LIST_RESPONSE),
240
+ msgFlags: new type.UInt16Le(0x01),
241
+ dataLen: new type.UInt32Le(0x00),
242
+ }));
243
+
244
+ this.sendFormatDataRequestPDU();
245
+ }
246
+
247
+
248
+ /**
249
+ * Receive format list response from server
250
+ * @param s {type.Stream}
251
+ */
252
+ recvFormatListResponsePDU(s) {
253
+ s.offset = 18;
254
+ // const pdu = data.clipPDU().read(s);
255
+ // console.log('recvFormatListResponsePDU', s);
256
+ // this.sendFormatDataRequestPDU();
257
+ }
258
+
259
+
260
+ /**
261
+ * Send format data request PDU
262
+ */
263
+ sendFormatDataRequestPDU(formartId = 0x0d) {
264
+ this.send(new type.Component({
265
+ msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_DATA_REQUEST),
266
+ msgFlags: new type.UInt16Le(0x00),
267
+ dataLen: new type.UInt32Le(0x04),
268
+ requestedFormatId: new type.UInt32Le(formartId),
269
+ }));
270
+ }
271
+
272
+
273
+ /**
274
+ * Receive format data request PDU from server
275
+ * @param s {type.Stream}
276
+ */
277
+ recvFormatDataRequestPDU(s) {
278
+ s.offset = 18;
279
+ // const pdu = data.clipPDU().read(s);
280
+ // console.log('recvFormatDataRequestPDU', s);
281
+ this.sendFormatDataResponsePDU();
282
+ }
283
+
284
+
285
+ /**
286
+ * Send format data reesponse PDU
287
+ */
288
+ sendFormatDataResponsePDU() {
289
+
290
+ const bufs = Buffer.from(this.content + '\x00' , 'ucs2');
291
+
292
+ this.send(new type.Component({
293
+ msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_DATA_RESPONSE),
294
+ msgFlags: new type.UInt16Le(0x01),
295
+ dataLen: new type.UInt32Le(bufs.length),
296
+ requestedFormatData: new type.BinaryString(bufs, { readLength : new type.CallableValue(bufs.length)})
297
+ }));
298
+
299
+ }
300
+
301
+
302
+ /**
303
+ * Receive format data response PDU from server
304
+ * @param s {type.Stream}
305
+ */
306
+ recvFormatDataResponsePDU(s) {
307
+ s.offset = 18;
308
+ // const pdu = data.clipPDU().read(s);
309
+ const str = s.buffer.toString('ucs2', 26, s.buffer.length-2);
310
+ // console.log('recvFormatDataResponsePDU', str);
311
+ this.content = str;
312
+ this.emit('clipboard', str)
313
+ }
314
+
315
+
316
+// =====================================================================================
317
+ setClipboardData(content) {
318
+ this.content = content;
319
+ this.sendFormatListPDU();
320
+ }
321
+
322
+}
323
+
324
+
325
+module.exports = {
326
+ Client
327
+}
rdp/protocol/pdu/data.js
+33
-1
@@ -1040,6 +1040,36 @@ function pdu(userId, pduMessage, opt) {
1040
return new type.Component(self, opt);
1041
}
1042
1043
+
1044
+const ClipPDUMsgType = {
1045
+ CB_MONITOR_READY: 0x0001,
1046
+ CB_FORMAT_LIST: 0x0002,
1047
+ CB_FORMAT_LIST_RESPONSE: 0x0003,
1048
+ CB_FORMAT_DATA_REQUEST: 0x0004,
1049
+ CB_FORMAT_DATA_RESPONSE: 0x0005,
1050
+ CB_TEMP_DIRECTORY: 0x0006,
1051
+ CB_CLIP_CAPS: 0x0007,
1052
+ CB_FILECONTENTS_REQUEST: 0x0008
1053
+}
1054
+
1055
+/**
1056
+ * @returns {type.Component}
1057
+ */
1058
+function clipPDU() {
1059
+ const self = {
1060
+ header: new type.Factory(function (s) {
1061
+ self.header = new type.Component({
1062
+ msgType: new type.UInt16Le().read(s),
1063
+ msgFlags: new type.UInt16Le().read(s),
1064
+ dataLen: new type.UInt32Le().read(s)
1065
+ })
1066
+ })
1067
+
1068
+ }
1069
+ return new type.Component(self);
1070
+}
1071
+
1072
+
1073
/**
1074
* @see http://msdn.microsoft.com/en-us/library/dd306368.aspx
1075
* @param opt {object} type option
@@ -1147,5 +1177,7 @@ module.exports = {
1177
updateDataPDU : updateDataPDU,
1178
dataPDU : dataPDU,
1179
fastPathBitmapUpdateDataPDU : fastPathBitmapUpdateDataPDU,
1150
- fastPathUpdatePDU : fastPathUpdatePDU
1180
+ fastPathUpdatePDU: fastPathUpdatePDU,
1181
+ clipPDU: clipPDU,
1182
+ ClipPDUMsgType: ClipPDUMsgType
1183
};
\ No newline at end of file
rdp/protocol/pdu/index.js
+6
-4
@@ -21,10 +21,12 @@ var lic = require('./lic');
21
var sec = require('./sec');
22
var global = require('./global');
23
var data = require('./data');
24
+var cliprdr = require('./cliprdr');
25
26
module.exports = {
26
- lic : lic,
27
- sec : sec,
28
- global : global,
29
- data : data
27
+ lic: lic,
28
+ sec: sec,
29
+ global: global,
30
+ data: data,
31
+ cliprdr: cliprdr
32
};
rdp/protocol/rdp.js
+12
@@ -87,6 +87,7 @@ function RdpClient(config) {
87
this.x224 = new x224.Client(this.tpkt, config);
88
this.mcs = new t125.mcs.Client(this.x224);
89
this.sec = new pdu.sec.Client(this.mcs, this.tpkt);
90
+ this.cliprdr = new pdu.cliprdr.Client(this.mcs);
91
this.global = new pdu.global.Client(this.sec, this.sec);
92
93
// config log level
@@ -145,6 +146,9 @@ function RdpClient(config) {
146
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
147
}
148
149
+ this.cliprdr.on('clipboard', (content) => {
150
+ this.emit('clipboard', content)
151
+ });
152
153
//bind all events
154
var self = this;
@@ -328,6 +332,14 @@ RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizon
332
this.global.sendInputEvents([event]);
333
}
334
335
+/**
336
+ * Clipboard event
337
+ * @param data {String} content for clipboard
338
+ */
339
+RdpClient.prototype.setClipboardData = function (content) {
340
+ this.cliprdr.setClipboardData(content);
341
+}
342
+
343
function createClient(config) {
344
return new RdpClient(config);
345
};
rdp/protocol/t125/mcs.js
+35
-6
@@ -25,6 +25,7 @@ var error = require('../../core').error;
25
var gcc = require('./gcc');
26
var per = require('./per');
27
var asn1 = require('../../asn1');
28
+var cliprdr = require('../pdu/cliprdr');
29
30
var Message = {
31
MCS_TYPE_CONNECT_INITIAL : 0x65,
@@ -43,10 +44,33 @@ var DomainMCSPDU = {
44
};
45
46
var Channel = {
46
- MCS_GLOBAL_CHANNEL : 1003,
47
- MCS_USERCHANNEL_BASE : 1001
47
+ MCS_GLOBAL_CHANNEL: 1003,
48
+ MCS_USERCHANNEL_BASE: 1001,
49
+ MCS_CLIPRDR_CHANNEL: 1005
50
};
51
52
+/**
53
+ * Channel Definde
54
+ */
55
+const RdpdrChannelDef = new type.Component({
56
+ name: new type.BinaryString(Buffer.from('rdpdr' + '\x00\x00\x00', 'binary'), { readLength: new type.CallableValue(8) }),
57
+ options: new type.UInt32Le(0x80800000)
58
+});
59
+
60
+const RdpsndChannelDef = new type.Component({
61
+ name: new type.BinaryString(Buffer.from('rdpsnd' + '\x00\x00', 'binary'), { readLength: new type.CallableValue(8) }),
62
+ options: new type.UInt32Le(0xc0000000)
63
+});
64
+
65
+const CliprdrChannelDef = new type.Component({
66
+ name: new type.BinaryString(Buffer.from('cliprdr' + '\x00', 'binary'), { readLength: new type.CallableValue(8) }),
67
+ // CHANNEL_OPTION_INITIALIZED |
68
+ // CHANNEL_OPTION_ENCRYPT_RDP |
69
+ // CHANNEL_OPTION_COMPRESS_RDP |
70
+ // CHANNEL_OPTION_SHOW_PROTOCOL
71
+ options: new type.UInt32Le(0xc0a00000)
72
+});
73
+
74
/**
75
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
76
* @returns {asn1.univ.Sequence}
@@ -126,7 +150,10 @@ function MCS(transport, recvOpCode, sendOpCode) {
150
this.transport = transport;
151
this.recvOpCode = recvOpCode;
152
this.sendOpCode = sendOpCode;
129
- this.channels = [{id : Channel.MCS_GLOBAL_CHANNEL, name : 'global'}];
153
+ this.channels = [
154
+ { id: Channel.MCS_GLOBAL_CHANNEL, name: 'global' },
155
+ { id: Channel.MCS_CLIPRDR_CHANNEL, name: 'cliprdr' }
156
+ ];
157
this.channels.find = function(callback) {
158
for(var i in this) {
159
if(callback(this[i])) return this[i];
@@ -207,8 +234,9 @@ function Client(transport) {
234
this.channelsConnected = 0;
235
236
// init gcc information
210
- this.clientCoreData = gcc.clientCoreData();
211
- this.clientNetworkData = gcc.clientNetworkData(new type.Component([]));
237
+ this.clientCoreData = gcc.clientCoreData();
238
+ // cliprdr channel
239
+ this.clientNetworkData = gcc.clientNetworkData(new type.Component([RdpdrChannelDef, CliprdrChannelDef, RdpsndChannelDef]));
240
this.clientSecurityData = gcc.clientSecurityData();
241
242
// must be readed from protocol
@@ -317,7 +345,8 @@ Client.prototype.recvChannelJoinConfirm = function(s) {
345
346
var channelId = per.readInteger16(s);
347
320
- if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
348
+ //if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
349
+ if ((confirm !== 0) && (channelId === Channel.MCS_CLIPRDR_CHANNEL || channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
350
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
351
}
352
rdp/security/md4.js
+1
-1
@@ -121,7 +121,7 @@
121
} else if (message.length === undefined) {
122
return method(message);
123
}
124
- return crypto.createHash('md4').update(new Buffer(message)).digest('hex');
124
+ return crypto.createHash('md4').update(Buffer.from(message)).digest('hex');
125
};
126
return nodeMethod;
127
};
views/default.handlebars
+10
-5
@@ -7566,7 +7566,7 @@
7566
if ((navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
7567
try {
7568
navigator.clipboard.readText().then(function(text) {
7569
- meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
7569
+ if (desktop.m.setClipboard) { desktop.m.setClipboard(text); } else { meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }
7570
}).catch(function(err) { console.log(err); });
7571
} catch (ex) { console.log(ex); }
7572
}
@@ -8332,7 +8332,8 @@
8332
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
8333
QE('connectbutton1h', hwonline);
8334
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
8335
- QE('DeskClip', (deskState == 3) && (desktop.contype != 4));
8335
+ QE('DeskClip', deskState == 3);
8336
+ //QE('DeskClip', (deskState == 3) && (desktop.contype != 4));
8337
QV('DeskClip', (inputAllowed) && (currentNode.agent) && ((features2 & 0x1800) != 0x1800) && (currentNode.agent.id != 11) && (currentNode.agent.id != 16) && ((desktop == null) || (desktop.contype != 2)) && ((desktopsettings.autoclipboard != true) || (navigator.clipboard == null) || (navigator.clipboard.readText == null))); // Clipboard not supported on macOS
8338
QE('DeskESC', (deskState == 3) && (desktop.contype != 4));
8339
QV('DeskESC', browserfullscreen && inputAllowed);
@@ -8737,7 +8738,7 @@
8738
try {
8739
navigator.clipboard.readText().then(function(text) {
8740
if ((text != null) && (deskLastClipboardSent != text)) {
8740
- meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
8741
+ if (desktop.m.setClipboard) { desktop.m.setClipboard(text); } else { meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }
8742
deskLastClipboardSent = text;
8743
}
8744
}).catch(function(err) { });
@@ -9323,8 +9324,12 @@
9324
9325
function showDeskClipSet() {
9326
if (desktop == null || desktop.State != 3) return;
9326
- meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
9327
- QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22) && (currentNode.agent.id != 34));
9327
+ if (desktop.m.setClipboard) {
9328
+ desktop.m.setClipboard(Q('d2clipText').value);
9329
+ } else {
9330
+ meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
9331
+ QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22) && (currentNode.agent.id != 34));
9332
+ }
9333
}
9334
9335
// Send CTRL-ALT-DEL