First integration of RDP into the desktop tab.
Ylian Saint-Hilaire committed
May 2, 2022 at 21:06 UTC
09881d06f545f33590129fb02369d3ab15b87c83
6 files changed
+260
-30
apprelays.js
+26
-16
@@ -62,10 +62,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
62
var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
63
if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
64
const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
65
- const user = parent.users[obj.cookie.userid];
65
+ const user = parent.users[obj.userid];
66
const username = (user != null) ? user.name : null;
67
- const event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.cookie.userid, username: username, sessionid: obj.sessionid, msgid: 125, msgArgs: [sessionSeconds, obj.sessionid], msg: "Left Web-RDP session \"" + obj.sessionid + "\" after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBRDP, bytesin: inTraffc, bytesout: outTraffc };
68
- parent.parent.DispatchEvent(['*', obj.nodeid, obj.cookie.userid, obj.meshid], obj, event);
67
+ const event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.userid, username: username, sessionid: obj.sessionid, msgid: 125, msgArgs: [sessionSeconds, obj.sessionid], msg: "Left Web-RDP session \"" + obj.sessionid + "\" after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBRDP, bytesin: inTraffc, bytesout: outTraffc };
68
+ parent.parent.DispatchEvent(['*', obj.nodeid, obj.userid, obj.meshid], obj, event);
69
delete obj.startTime;
70
delete obj.sessionid;
71
}
@@ -129,8 +129,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
129
function startRdp(port) {
130
parent.parent.debug('relay', 'RDP: Starting RDP client on loopback port ' + port);
131
try {
132
- //rdpClient = require('node-rdpjs-2').createClient({
133
- rdpClient = require('./rdp').createClient({
132
+ const args = {
133
logLevel: 'NONE', // 'ERROR',
134
domain: obj.infos.domain,
135
userName: obj.infos.username,
@@ -139,7 +138,8 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
138
autoLogin: true,
139
screen: obj.infos.screen,
140
locale: obj.infos.locale
142
- }).on('connect', function () {
141
+ };
142
+ rdpClient = require('./rdp').createClient(args).on('connect', function () {
143
send(['rdp-connect']);
144
if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed
145
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
@@ -147,10 +147,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
147
148
// Event session start
149
try {
150
- const user = parent.users[obj.cookie.userid];
150
+ const user = parent.users[obj.userid];
151
const username = (user != null) ? user.name : null;
152
- const event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.cookie.userid, username: username, sessionid: obj.sessionid, msgid: 150, msgArgs: [obj.sessionid], msg: "Started Web-RDP session \"" + obj.sessionid + "\".", protocol: PROTOCOL_WEBRDP };
153
- parent.parent.DispatchEvent(['*', obj.nodeid, obj.cookie.userid, obj.meshid], obj, event);
152
+ const event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.userid, username: username, sessionid: obj.sessionid, msgid: 150, msgArgs: [obj.sessionid], msg: "Started Web-RDP session \"" + obj.sessionid + "\".", protocol: PROTOCOL_WEBRDP };
153
+ parent.parent.DispatchEvent(['*', obj.nodeid, obj.userid, obj.meshid], obj, event);
154
} catch (ex) { console.log(ex); }
155
}).on('bitmap', function (bitmap) {
156
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
@@ -201,11 +201,17 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
201
case 'infos': {
202
obj.infos = msg[1];
203
204
- // Decode the authentication cookie
205
- obj.cookie = parent.parent.decodeCookie(obj.infos.ip, parent.parent.loginCookieEncryptionKey);
206
- if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) { obj.close(); return; }
207
- obj.nodeid = obj.cookie.nodeid;
208
- obj.userid = obj.cookie.userid;
204
+ if (obj.infos.ip.startsWith('node/')) {
205
+ // Use the user session
206
+ obj.nodeid = obj.infos.ip;
207
+ obj.userid = req.session.userid;
208
+ } else {
209
+ // Decode the authentication cookie
210
+ obj.cookie = parent.parent.decodeCookie(obj.infos.ip, parent.parent.loginCookieEncryptionKey);
211
+ if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) { obj.close(); return; }
212
+ obj.nodeid = obj.cookie.nodeid;
213
+ obj.userid = obj.cookie.userid;
214
+ }
215
216
// Get node
217
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
@@ -222,7 +228,11 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
228
obj.tcpaddr = node.host;
229
230
// Re-encode a cookie with a device relay
225
- const cookieContent = { userid: obj.cookie.userid, domainid: obj.cookie.domainid, nodeid: mesh.relayid, tcpaddr: node.host, tcpport: obj.cookie.tcpport };
231
+ const cookieContent = { userid: obj.userid, domainid: domain.id, nodeid: mesh.relayid, tcpaddr: node.host, tcpport: obj.cookie.tcpport };
232
+ obj.infos.ip = parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey);
233
+ } else if (obj.infos.ip.startsWith('node/')) {
234
+ // Encode a cookie with a device relay
235
+ const cookieContent = { userid: obj.userid, domainid: domain.id, nodeid: obj.nodeid, tcpport: node.rdpport ? node.rdpport : 3389 };
236
obj.infos.ip = parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey);
237
}
238
@@ -234,7 +244,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
244
// Check if we need to load server stored credentials
245
if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) {
246
// Check if RDP credentials exist
237
- if ((domain.allowsavingdevicecredentials === false) && (typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) {
247
+ if ((domain.allowsavingdevicecredentials !== false) && (typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) {
248
obj.infos.domain = node.rdp.d;
249
obj.infos.username = node.rdp.u;
250
obj.infos.password = node.rdp.p;
public/mstsc/client.js
+7
-1
@@ -138,7 +138,13 @@
138
});
139
140
return this;
141
- },
141
+ },
142
+ /**
143
+ * disconnect
144
+ */
145
+ disconnect: function () {
146
+ if (this.socket) { this.socket.close(); }
147
+ },
148
/**
149
* connect
150
* @param ip {string} ip target for rdp
public/scripts/agent-desktop-0.0.2.js
+1
-1
@@ -560,7 +560,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
560
561
obj.onResize = function () {
562
if (obj.ScreenWidth == 0 || obj.ScreenHeight == 0) return;
563
- if (obj.Canvas.canvas.width == obj.ScreenWidth && obj.Canvas.canvas.height == obj.ScreenHeight) return;
563
+ if ((obj.Canvas.canvas.width == obj.ScreenWidth) && (obj.Canvas.canvas.height == obj.ScreenHeight)) return;
564
if (obj.FirstDraw) {
565
obj.Canvas.canvas.width = obj.ScreenWidth;
566
obj.Canvas.canvas.height = obj.ScreenHeight;
public/scripts/agent-rdp-0.0.1.js
new
+159
@@ -0,0 +1,159 @@
1
+/**
2
+* @description RDP Remote Desktop
3
+* @author Ylian Saint-Hilaire
4
+* @version v0.0.1
5
+*/
6
+
7
+// Construct a RDP remote desktop object
8
+var CreateRDPDesktop = function (canvasid) {
9
+ var obj = {}
10
+ obj.m = {};
11
+ obj.State = 0;
12
+ obj.canvas = Q(canvasid);
13
+ obj.CanvasId = canvasid;
14
+ if (typeof canvasid === 'string') obj.CanvasId = Q(canvasid);
15
+ obj.Canvas = obj.CanvasId.getContext('2d');
16
+ obj.ScreenWidth = obj.width = 1280;
17
+ obj.ScreenHeight = obj.height = 1024;
18
+
19
+ function mouseButtonMap(button) {
20
+ // Swap mouse buttons if needed
21
+ if (obj.m.SwapMouse === true) return [2, 0, 1, 0, 0][button];
22
+ return [1, 0, 2, 0, 0][button];
23
+ };
24
+
25
+ obj.Start = function (nodeid, port, credentials) {
26
+ changeState(1);
27
+ obj.nodeid = nodeid;
28
+ obj.port = port;
29
+ obj.credentials = credentials;
30
+ var options = { savepass: credentials.savecred, useServerCreds: credentials.servercred };
31
+ obj.render = new Mstsc.Canvas.create(obj.canvas);
32
+ obj.socket = new WebSocket('wss://' + window.location.host + '/mstscrelay.ashx'); // TODO: Support domains
33
+ obj.socket.binaryType = 'arraybuffer';
34
+ obj.socket.onopen = function () {
35
+ changeState(2); // Setup state
36
+ obj.socket.send(JSON.stringify(['infos', {
37
+ ip: obj.nodeid,
38
+ port: obj.port,
39
+ screen: { width: obj.width, height: obj.height },
40
+ domain: credentials.domain,
41
+ username: credentials.username,
42
+ password: credentials.password,
43
+ options: options,
44
+ locale: Mstsc.locale()
45
+ }]));
46
+ };
47
+ obj.socket.onmessage = function (evt) {
48
+ if (typeof evt.data == 'string') {
49
+ // This is a JSON text string, parse it.
50
+ var msg = JSON.parse(evt.data);
51
+ switch (msg[0]) {
52
+ case 'rdp-connect': {
53
+ changeState(3);
54
+ obj.rotation = 0;
55
+ obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
56
+ obj.Canvas.canvas.width = obj.ScreenWidth;
57
+ obj.Canvas.canvas.height = obj.ScreenHeight;
58
+ obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
59
+ if (obj.m.onScreenSizeChange != null) { obj.m.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
60
+ break;
61
+ }
62
+ case 'rdp-bitmap': {
63
+ if (obj.bitmapData == null) break;
64
+ var bitmap = msg[1];
65
+ bitmap.data = obj.bitmapData; // Use the binary data that was sent earlier.
66
+ delete obj.bitmapData;
67
+ obj.render.update(bitmap);
68
+ break;
69
+ }
70
+ case 'rdp-close': {
71
+ obj.Stop();
72
+ break;
73
+ }
74
+ case 'rdp-error': {
75
+ var err = msg[1];
76
+ console.log('[mstsc.js] error : ' + err.code + '(' + err.message + ')');
77
+ obj.Stop();
78
+ break;
79
+ }
80
+ }
81
+ } else {
82
+ // This is binary bitmap data, store it.
83
+ obj.bitmapData = evt.data;
84
+ }
85
+ };
86
+ obj.socket.onclose = function () { changeState(0); };
87
+ changeState(1);
88
+ }
89
+
90
+ obj.Stop = function () {
91
+ if (obj.socket) { obj.socket.close(); }
92
+ }
93
+
94
+ function changeState(newstate) {
95
+ if (obj.State == newstate) return;
96
+ obj.State = newstate;
97
+ if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
98
+ }
99
+
100
+ function getPositionOfControl(Control) {
101
+ var Position = Array(2);
102
+ Position[0] = Position[1] = 0;
103
+ while (Control) { Position[0] += Control.offsetLeft; Position[1] += Control.offsetTop; Control = Control.offsetParent; }
104
+ return Position;
105
+ }
106
+
107
+ function getMousePosition(event) {
108
+ var ScaleFactorHeight = (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
109
+ var ScaleFactorWidth = (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
110
+ var Offsets = getPositionOfControl(obj.Canvas.canvas);
111
+ var X = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
112
+ var Y = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
113
+ if (event.addx) { X += event.addx; }
114
+ if (event.addy) { Y += event.addy; }
115
+ return { x: X, y: Y };
116
+ }
117
+
118
+ obj.m.mousemove = function (e) {
119
+ if (!obj.socket || (obj.State != 3)) return;
120
+ var m = getMousePosition(e);
121
+ obj.mouseNagleData = ['mouse', m.x, m.y, 0, false];
122
+ if (obj.mouseNagleTimer == null) { obj.mouseNagleTimer = setTimeout(function () { obj.socket.send(JSON.stringify(obj.mouseNagleData)); obj.mouseNagleTimer = null; }, 50); }
123
+ e.preventDefault();
124
+ return false;
125
+ }
126
+ obj.m.mouseup = function (e) {
127
+ if (!obj.socket || (obj.State != 3)) return;
128
+ if (obj.mouseNagleTimer != null) { clearTimeout(obj.mouseNagleTimer); obj.mouseNagleTimer = null; }
129
+ var m = getMousePosition(e);
130
+ obj.socket.send(JSON.stringify(['mouse', m.x, m.y, mouseButtonMap(e.button), false]));
131
+ e.preventDefault();
132
+ return false;
133
+ }
134
+ obj.m.mousedown = function (e) {
135
+ if (!obj.socket || (obj.State != 3)) return;
136
+ if (obj.mouseNagleTimer != null) { clearTimeout(obj.mouseNagleTimer); obj.mouseNagleTimer = null; }
137
+ var m = getMousePosition(e);
138
+ obj.socket.send(JSON.stringify(['mouse', m.x, m.y, mouseButtonMap(e.button), true]));
139
+ e.preventDefault();
140
+ return false;
141
+ }
142
+ obj.m.handleKeyUp = function (e) {
143
+ if (!obj.socket || (obj.State != 3)) return;
144
+ obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), false]));
145
+ e.preventDefault();
146
+ return false;
147
+ }
148
+ obj.m.handleKeyDown = function (e) {
149
+ if (!obj.socket || (obj.State != 3)) return;
150
+ obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), true]));
151
+ e.preventDefault();
152
+ return false;
153
+ }
154
+ obj.m.mousedblclick = function () { }
155
+ obj.m.handleKeyPress = function () { }
156
+ obj.m.setRotation = function () { }
157
+
158
+ return obj;
159
+}
translate/translate.js
+1
@@ -81,6 +81,7 @@ var minifyMeshCentralSourceFiles = [
81
"../views/mstsc.handlebars",
82
"../views/ssh.handlebars",
83
"../public/scripts/agent-desktop-0.0.2.js",
84
+ "../public/scripts/agent-rdp-0.0.1.js",
85
"../public/scripts/agent-redir-rtc-0.1.0.js",
86
"../public/scripts/agent-redir-ws-0.1.1.js",
87
"../public/scripts/amt-0.2.0.js",
views/default.handlebars
+66
-12
@@ -29,10 +29,16 @@
29
<script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script>
30
<script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0{{{min}}}.js"></script>
31
<script type="text/javascript" src="scripts/agent-desktop-0.0.2{{{min}}}.js"></script>
32
+ <script type="text/javascript" src="scripts/agent-rdp-0.0.1{{{min}}}.js"></script>
33
<script type="text/javascript" src="scripts/qrcode.min.js"></script>
34
<script type="text/javascript" src="scripts/xterm{{{min}}}.js"></script>
35
<script type="text/javascript" src="scripts/xterm-addon-fit{{{min}}}.js"></script>
36
<script type="text/javascript" src="scripts/flatpickr.js"></script>
37
+ <script type="text/javascript" src="mstsc/mstsc.js"></script>
38
+ <script type="text/javascript" src="mstsc/keyboard.js"></script>
39
+ <script type="text/javascript" src="mstsc/rle.js"></script>
40
+ <script type="text/javascript" src="mstsc/client.js"></script>
41
+ <script type="text/javascript" src="mstsc/canvas.js"></script>
42
<script keeplink=1 type="text/javascript" src="scripts/u2f-api{{{min}}}.js"></script>
43
<script keeplink=1 type="text/javascript" src="scripts/charts{{{min}}}.js"></script>
44
<script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script>
@@ -82,9 +88,10 @@
88
<div class="cmtext" onclick="cmtermaction(100,event)">Login Shell</div>
89
</div>
90
<div id="deskConnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
85
- <div class="cmtext" onclick="cmdeskaction(1,event)">Ask Consent + Bar</div>
86
- <div class="cmtext" onclick="cmdeskaction(2,event)">Ask Consent</div>
87
- <div class="cmtext" onclick="cmdeskaction(3,event)">Privacy Bar</div>
91
+ <div id="deskConnectContextMenu1" class="cmtext" onclick="cmdeskaction(1,event)">Ask Consent + Bar</div>
92
+ <div id="deskConnectContextMenu2" class="cmtext" onclick="cmdeskaction(2,event)">Ask Consent</div>
93
+ <div id="deskConnectContextMenu3" class="cmtext" onclick="cmdeskaction(3,event)">Privacy Bar</div>
94
+ <div id="deskConnectContextMenu4" class="cmtext" onclick="cmdeskaction(4,event)">RDP</div>
95
</div>
96
<div id="deskDisconnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
97
<div class="cmtext" onclick="cmdeskaction(10,event)">Disconnect and Lock</div>
@@ -5771,6 +5778,10 @@
5778
// Desktop connect button context menu
5779
if ((currentNode == null) || (currentNode.agent == null)) return true;
5780
contextelement = elem;
5781
+ QV('deskConnectContextMenu1', currentNode.mtype == 2); // Ask Consent + Bar
5782
+ QV('deskConnectContextMenu2', currentNode.mtype == 2); // Ask Consent
5783
+ QV('deskConnectContextMenu3', currentNode.mtype == 2); // Privacy Bar
5784
+ QV('deskConnectContextMenu4', (currentNode.agent.id == 3) || (currentNode.agent.id == 4)); // RDP
5785
showContextMenuDiv(document.getElementById('deskConnectContextMenu'), event.pageX, event.pageY);
5786
break;
5787
}
@@ -5916,6 +5927,7 @@
5927
if (action == 1) { connectDesktop(null, 3, null, 0x0008 + 0x0040); } // Consent Prompt + Privacy bar
5928
if (action == 2) { connectDesktop(null, 3, null, 0x0008); } // Consent Prompt
5929
if (action == 3) { connectDesktop(null, 3, null, 0x0040); } // Privacy bar
5930
+ if (action == 4) { askRdpCredentials(); } // RDP
5931
if (action == 10) { if (desktop != null) { desktop.sendCtrlMsg('{"ctrlChannel":"102938","type":"autolock","value":true}'); connectDesktop(); } } // Disconnect and lock
5932
if (action == 11) { if (desktop != null) { desktop.sendCtrlMsg('{"ctrlChannel":"102938","type":"autolock","value":false}'); connectDesktop(); } } // Disconnect without lock
5933
}
@@ -8273,15 +8285,16 @@
8285
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
8286
QE('connectbutton1h', hwonline);
8287
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
8276
- QE('DeskClip', deskState == 3);
8288
+ QE('DeskClip', (deskState == 3) && (desktop.contype != 4));
8289
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
8278
- QE('DeskESC', deskState == 3);
8290
+ QE('DeskESC', (deskState == 3) && (desktop.contype != 4));
8291
QV('DeskESC', browserfullscreen && inputAllowed);
8280
- QE('DeskType', deskState == 3);
8292
+ QE('DeskType', (deskState == 3) && (desktop.contype != 4));
8293
QV('DeskType', inputAllowed);
8282
- QE('DeskWD', deskState == 3);
8294
+ QE('DeskWD', (deskState == 3) && (desktop.contype != 4));
8295
QV('DeskWD', inputAllowed);
8296
QV('deskkeys', inputAllowed);
8297
+ QE('deskkeys', (desktop != null) && (desktop.contype != 4));
8298
QV('DeskTimer', deskState == 3);
8299
8300
// Enable browser clipboard read if supported
@@ -8467,6 +8480,14 @@
8480
} else if (contype == 3) {
8481
// Ask for user sessions
8482
meshserver.send({ action: 'msg', type: 'userSessions', nodeid: currentNode._id, tag: consent });
8483
+ } else if (contype == 4) {
8484
+ // Setup RDP remote desktop
8485
+ desktop = CreateRDPDesktop('Desk');
8486
+ desktop.onStateChanged = onDesktopStateChange;
8487
+ desktop.m.onScreenSizeChange = mdeskAdjust;
8488
+ if (desktopsettings.swapmouse) { desktop.m.SwapMouse = desktopsettings.swapmouse; }
8489
+ desktop.Start(desktopNode._id, currentNode.rdpport ? currentNode.rdpport : 3389, tsid);
8490
+ desktop.contype = 4;
8491
}
8492
} else {
8493
// Disconnect and clean up the remote desktop
@@ -8477,6 +8498,37 @@
8498
}
8499
}
8500
8501
+ function askRdpCredentials() {
8502
+ if (xxdialogMode) return;
8503
+ var x = '<div>';
8504
+ if (currentNode.rdp == 1) {
8505
+ x += addHtmlValue("Credentials", '<select id=d2mode style=width:230px onchange=askRdpCredentialsValidate()><option value=1>' + "Use server credentials" + '</option><option value=2>' + "Use new credentials" + '</option></select>');
8506
+ x += '<div id=d2cred style=display:none>';
8507
+ } else {
8508
+ x += '<div id=d2cred>';
8509
+ }
8510
+ x += addHtmlValue("Domain", '<input type=text id=d2domain style=width:230px maxlength=128 />');
8511
+ x += addHtmlValue("Username", '<input type=text id=d2user style=width:230px onKeyUp=askRdpCredentialsValidate() maxlength=128 />');
8512
+ x += addHtmlValue("Password", '<input type=password id=d2pass style=width:230px maxlength=128 autocomplete=off />');
8513
+ x += addHtmlValue("", '<label><input type="checkbox" id=d2savecred>' + "Remember credentials" + '</label>');
8514
+ x + '</div></div>';
8515
+ setDialogMode(2, "RDP Credentials", 3, askRdpCredentialsEx, x);
8516
+ }
8517
+
8518
+ function askRdpCredentialsValidate() {
8519
+ var ok = true;
8520
+ if (currentNode.rdp == 1) { QV('d2cred', Q('d2mode').value == 2); if (Q('d2mode').value == 1) { QE('idx_dlgOkButton', true); return; } }
8521
+ QE('idx_dlgOkButton', Q('d2user').value != '');
8522
+ }
8523
+
8524
+ function askRdpCredentialsEx() {
8525
+ if ((currentNode.rdp == 1) && (Q('d2mode').value == 1)) {
8526
+ connectDesktop(null, 4, { servercred: true });
8527
+ } else {
8528
+ connectDesktop(null, 4, { domain: Q('d2domain').value, username: Q('d2user').value, password: Q('d2pass').value, savecred: Q('d2savecred').checked });
8529
+ }
8530
+ }
8531
+
8532
function updateMetadata(conn, elementid) {
8533
var str = '', viewerCount = 0;
8534
if (conn && (conn.State == 3)) {
@@ -8559,15 +8611,17 @@
8611
if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
8612
var str = StatusStrs[xstate];
8613
if ((desktop != null) && (desktop.webRtcActive == true)) { str += ", WebRTC"; }
8614
+ if ((desktop != null) && (xstate == 3) && (desktop.contype == 4)) { str += ", RDP"; }
8615
//if (desktop.m.stopInput == true) { str += ', Loopback'; }
8616
QH('deskstatus', str);
8617
switch (state) {
8618
case 0:
8566
- // Stop recording
8567
- if (desktop.m.recordedData != null) { deskRecordSession(); }
8568
-
8569
- // Disconnect and clean up the remote desktop
8570
- desktop.Stop();
8619
+ if (desktop != null) {
8620
+ // Stop recording
8621
+ if (desktop.m.recordedData != null) { deskRecordSession(); }
8622
+ // Disconnect and clean up the remote desktop
8623
+ desktop.Stop();
8624
+ }
8625
desktopNode = desktop = null;
8626
QV('DeskFocus', false);
8627
QV('DeskMonitorSelectionSpan', false);