Added RDP alternate shell / working dir, #3943

Ylian Saint-Hilaire committed May 8, 2022 at 11:46 UTC 6797f8199c2c6ef2504a71bedd39c662b18d25bc
4 files changed +243 -228
apprelays.js
+5 -1
@@ -179,7 +179,11 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
179 screen: obj.infos.screen,
180 locale: obj.infos.locale,
181 };
182 - if (obj.infos.options && (obj.infos.options.flags != null)) { args.perfFlags = obj.infos.options.flags; delete obj.infos.options.flags; }
182 + if (obj.infos.options) {
183 + if (obj.infos.options.flags != null) { args.perfFlags = obj.infos.options.flags; delete obj.infos.options.flags; }
184 + if ((obj.infos.options.workingDir != null) && (obj.infos.options.workingDir != '')) { args.workingDir = obj.infos.options.workingDir; }
185 + if ((obj.infos.options.alternateShell != null) && (obj.infos.options.alternateShell != '')) { args.alternateShell = obj.infos.options.alternateShell; }
186 + }
187 rdpClient = require('./rdp').createClient(args).on('connect', function () {
188 send(['rdp-connect']);
189 if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed
public/scripts/agent-rdp-0.0.1.js
+1 -1
@@ -27,7 +27,7 @@ var CreateRDPDesktop = function (canvasid) {
27 obj.nodeid = nodeid;
28 obj.port = port;
29 obj.credentials = credentials;
30 - var options = { savepass: credentials.savecred, useServerCreds: credentials.servercred, width: credentials.width, height: credentials.height, flags: credentials.flags };
30 + var options = { savepass: credentials.savecred, useServerCreds: credentials.servercred, width: credentials.width, height: credentials.height, flags: credentials.flags, workingDir: credentials.workdir, alternateShell: credentials.altshell };
31 if (credentials.width && credentials.height) {
32 options.width = obj.ScreenWidth = obj.width = credentials.width;
33 options.height = obj.ScreenHeight = obj.height = credentials.height;
rdp/protocol/rdp.js
+221 -221
@@ -33,81 +33,81 @@ var pdu = require('./pdu');
33 * decompress bitmap from RLE algorithm
34 * @param bitmap {object} bitmap object of bitmap event of node-rdpjs
35 */
36 -function decompress (bitmap) {
37 - var fName = null;
38 - switch (bitmap.bitsPerPixel.value) {
39 - case 15:
40 - fName = 'bitmap_decompress_15';
41 - break;
42 - case 16:
43 - fName = 'bitmap_decompress_16';
44 - break;
45 - case 24:
46 - fName = 'bitmap_decompress_24';
47 - break;
48 - case 32:
49 - fName = 'bitmap_decompress_32';
50 - break;
51 - default:
52 - throw 'invalid bitmap data format';
53 - }
54 -
55 - var input = new Uint8Array(bitmap.bitmapDataStream.value);
56 - var inputPtr = rle._malloc(input.length);
57 - var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length);
58 - inputHeap.set(input);
59 -
60 - var ouputSize = bitmap.width.value * bitmap.height.value * 4;
61 - var outputPtr = rle._malloc(ouputSize);
62 -
63 - var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize);
64 -
65 - var res = rle.ccall(fName,
66 - 'number',
67 - ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
68 - [outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length]
69 - );
70 -
71 - var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize);
72 -
73 - rle._free(inputPtr);
74 - rle._free(outputPtr);
75 -
76 - return output;
36 +function decompress(bitmap) {
37 + var fName = null;
38 + switch (bitmap.bitsPerPixel.value) {
39 + case 15:
40 + fName = 'bitmap_decompress_15';
41 + break;
42 + case 16:
43 + fName = 'bitmap_decompress_16';
44 + break;
45 + case 24:
46 + fName = 'bitmap_decompress_24';
47 + break;
48 + case 32:
49 + fName = 'bitmap_decompress_32';
50 + break;
51 + default:
52 + throw 'invalid bitmap data format';
53 + }
54 +
55 + var input = new Uint8Array(bitmap.bitmapDataStream.value);
56 + var inputPtr = rle._malloc(input.length);
57 + var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length);
58 + inputHeap.set(input);
59 +
60 + var ouputSize = bitmap.width.value * bitmap.height.value * 4;
61 + var outputPtr = rle._malloc(ouputSize);
62 +
63 + var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize);
64 +
65 + var res = rle.ccall(fName,
66 + 'number',
67 + ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
68 + [outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length]
69 + );
70 +
71 + var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize);
72 +
73 + rle._free(inputPtr);
74 + rle._free(outputPtr);
75 +
76 + return output;
77 }
78
79 /**
80 * Main RDP module
81 */
82 function RdpClient(config) {
83 - config = config || {};
84 - this.connected = false;
85 - this.bufferLayer = new layer.BufferLayer(new net.Socket());
86 - this.tpkt = new TPKT(this.bufferLayer);
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.global = new pdu.global.Client(this.sec, this.sec);
91 -
92 - // config log level
93 - log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO;
94 -
95 - // credentials
96 - if (config.domain) {
97 - this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2');
98 - }
99 - if (config.userName) {
100 - this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2');
101 - }
102 - if (config.password) {
103 - this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2');
104 - }
105 - if(config.workingDir) {
106 - this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2');
107 - }
108 - if(config.alternateShell) {
109 - this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2');
110 - }
83 + config = config || {};
84 + this.connected = false;
85 + this.bufferLayer = new layer.BufferLayer(new net.Socket());
86 + this.tpkt = new TPKT(this.bufferLayer);
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.global = new pdu.global.Client(this.sec, this.sec);
91 +
92 + // config log level
93 + log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO;
94 +
95 + // credentials
96 + if (config.domain) {
97 + this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2');
98 + }
99 + if (config.userName) {
100 + this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2');
101 + }
102 + if (config.password) {
103 + this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2');
104 + }
105 + if (config.workingDir) {
106 + this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2');
107 + }
108 + if (config.alternateShell) {
109 + this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2');
110 + }
111
112 if (config.perfFlags != null) {
113 this.sec.infos.obj.extendedInfo.obj.performanceFlags.value = config.perfFlags;
@@ -121,72 +121,72 @@ function RdpClient(config) {
121 | pdu.sec.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG;
122 }
123 }
124 -
125 - if (config.autoLogin) {
126 - this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON;
127 - }
128 -
129 - if (config.screen && config.screen.width && config.screen.height) {
130 - this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width;
131 - this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height;
132 - }
133 -
134 - log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value);
135 -
136 - // config keyboard layout
137 - switch (config.locale) {
138 - case 'fr':
139 - log.debug('french keyboard layout');
140 - this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH;
141 - break;
142 - case 'en':
143 - default:
144 - log.debug('english keyboard layout');
145 - this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
146 - }
147 -
148 -
149 - //bind all events
150 - var self = this;
151 - this.global.on('connect', function () {
152 - self.connected = true;
153 - self.emit('connect');
154 - }).on('session', function () {
155 - self.emit('session');
156 - }).on('close', function () {
157 - self.connected = false;
158 - self.emit('close');
159 - }).on('bitmap', function (bitmaps) {
160 - for(var bitmap in bitmaps) {
161 - var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value;
162 - var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION;
163 -
164 - if (isCompress && config.decompress) {
165 - bitmapData = decompress(bitmaps[bitmap].obj);
166 - isCompress = false;
167 - }
168 -
169 - self.emit('bitmap', {
170 - destTop : bitmaps[bitmap].obj.destTop.value,
171 - destLeft : bitmaps[bitmap].obj.destLeft.value,
172 - destBottom : bitmaps[bitmap].obj.destBottom.value,
173 - destRight : bitmaps[bitmap].obj.destRight.value,
174 - width : bitmaps[bitmap].obj.width.value,
175 - height : bitmaps[bitmap].obj.height.value,
176 - bitsPerPixel : bitmaps[bitmap].obj.bitsPerPixel.value,
177 - isCompress : isCompress,
178 - data : bitmapData
179 - });
180 - }
181 - }).on('error', function (err) {
182 - log.warn(err.code + '(' + err.message + ')\n' + err.stack);
183 - if (err instanceof error.FatalError) {
184 - throw err;
185 - }
186 - else {
187 - self.emit('error', err);
188 - }
189 - });
124 +
125 + if (config.autoLogin) {
126 + this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON;
127 + }
128 +
129 + if (config.screen && config.screen.width && config.screen.height) {
130 + this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width;
131 + this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height;
132 + }
133 +
134 + log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value);
135 +
136 + // config keyboard layout
137 + switch (config.locale) {
138 + case 'fr':
139 + log.debug('french keyboard layout');
140 + this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH;
141 + break;
142 + case 'en':
143 + default:
144 + log.debug('english keyboard layout');
145 + this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
146 + }
147 +
148 +
149 + //bind all events
150 + var self = this;
151 + this.global.on('connect', function () {
152 + self.connected = true;
153 + self.emit('connect');
154 + }).on('session', function () {
155 + self.emit('session');
156 + }).on('close', function () {
157 + self.connected = false;
158 + self.emit('close');
159 + }).on('bitmap', function (bitmaps) {
160 + for (var bitmap in bitmaps) {
161 + var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value;
162 + var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION;
163 +
164 + if (isCompress && config.decompress) {
165 + bitmapData = decompress(bitmaps[bitmap].obj);
166 + isCompress = false;
167 + }
168 +
169 + self.emit('bitmap', {
170 + destTop: bitmaps[bitmap].obj.destTop.value,
171 + destLeft: bitmaps[bitmap].obj.destLeft.value,
172 + destBottom: bitmaps[bitmap].obj.destBottom.value,
173 + destRight: bitmaps[bitmap].obj.destRight.value,
174 + width: bitmaps[bitmap].obj.width.value,
175 + height: bitmaps[bitmap].obj.height.value,
176 + bitsPerPixel: bitmaps[bitmap].obj.bitsPerPixel.value,
177 + isCompress: isCompress,
178 + data: bitmapData
179 + });
180 + }
181 + }).on('error', function (err) {
182 + log.warn(err.code + '(' + err.message + ')\n' + err.stack);
183 + if (err instanceof error.FatalError) {
184 + throw err;
185 + }
186 + else {
187 + self.emit('error', err);
188 + }
189 + });
190 }
191
192 inherits(RdpClient, events.EventEmitter);
@@ -197,24 +197,24 @@ inherits(RdpClient, events.EventEmitter);
197 * @param port {integer} destination port
198 */
199 RdpClient.prototype.connect = function (host, port) {
200 - log.debug('connect to ' + host + ':' + port);
201 - var self = this;
202 - this.bufferLayer.socket.connect(port, host, function () {
203 - // in client mode connection start from x224 layer
204 - self.x224.connect();
205 - });
206 - return this;
200 + log.debug('connect to ' + host + ':' + port);
201 + var self = this;
202 + this.bufferLayer.socket.connect(port, host, function () {
203 + // in client mode connection start from x224 layer
204 + self.x224.connect();
205 + });
206 + return this;
207 };
208
209 /**
210 * Close RDP client
211 */
212 RdpClient.prototype.close = function () {
213 - if(this.connected) {
214 - this.global.close();
215 - }
216 - this.connected = false;
217 - return this;
213 + if (this.connected) {
214 + this.global.close();
215 + }
216 + this.connected = false;
217 + return this;
218 };
219
220 /**
@@ -225,31 +225,31 @@ RdpClient.prototype.close = function () {
225 * @param isPressed {boolean} state of button
226 */
227 RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) {
228 - if (!this.connected)
229 - return;
230 -
231 - var event = pdu.data.pointerEvent();
232 - if (isPressed) {
233 - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN;
234 - }
235 -
236 - switch(button) {
237 - case 1:
238 - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1;
239 - break;
240 - case 2:
241 - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2;
242 - break;
243 - case 3:
244 - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
245 - break;
246 - default:
247 - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE;
248 - }
249 -
228 + if (!this.connected)
229 + return;
230 +
231 + var event = pdu.data.pointerEvent();
232 + if (isPressed) {
233 + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN;
234 + }
235 +
236 + switch (button) {
237 + case 1:
238 + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1;
239 + break;
240 + case 2:
241 + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2;
242 + break;
243 + case 3:
244 + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
245 + break;
246 + default:
247 + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE;
248 + }
249 +
250 event.obj.xPos.value = x;
251 event.obj.yPos.value = y;
252 -
252 +
253 this.global.sendInputEvents([event]);
254 };
255
@@ -260,20 +260,20 @@ RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) {
260 * @param extended {boolenan} extended keys
261 */
262 RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) {
263 - if (!this.connected)
264 - return;
265 - extended = extended || false;
266 - var event = pdu.data.scancodeKeyEvent();
267 - event.obj.keyCode.value = code;
268 -
269 - if (!isPressed) {
270 - event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
271 - }
272 -
263 + if (!this.connected)
264 + return;
265 + extended = extended || false;
266 + var event = pdu.data.scancodeKeyEvent();
267 + event.obj.keyCode.value = code;
268 +
269 + if (!isPressed) {
270 + event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
271 + }
272 +
273 if (extended) {
274 - event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED;
274 + event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED;
275 }
276 -
276 +
277 this.global.sendInputEvents([event]);
278 };
279
@@ -283,16 +283,16 @@ RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended)
283 * @param isPressed {boolean}
284 */
285 RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) {
286 - if (!this.connected)
287 - return;
288 -
289 - var event = pdu.data.unicodeKeyEvent();
290 - event.obj.unicode.value = code;
291 -
292 - if (!isPressed) {
293 - event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
294 - }
295 - this.global.sendInputEvents([event]);
286 + if (!this.connected)
287 + return;
288 +
289 + var event = pdu.data.unicodeKeyEvent();
290 + event.obj.unicode.value = code;
291 +
292 + if (!isPressed) {
293 + event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
294 + }
295 + this.global.sendInputEvents([event]);
296 }
297
298 /**
@@ -304,32 +304,32 @@ RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) {
304 * @param isHorizontal {boolean}
305 */
306 RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizontal) {
307 - if (!this.connected)
308 - return;
309 -
310 - var event = pdu.data.pointerEvent();
311 - if (isHorizontal) {
307 + if (!this.connected)
308 + return;
309 +
310 + var event = pdu.data.pointerEvent();
311 + if (isHorizontal) {
312 event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL;
313 - }
313 + }
314 else {
315 - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL;
315 + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL;
316 }
317 -
318 -
319 - if (isNegative) {
317 +
318 +
319 + if (isNegative) {
320 event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE;
321 - }
322 -
321 + }
322 +
323 event.obj.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask)
324 -
324 +
325 event.obj.xPos.value = x;
326 event.obj.yPos.value = y;
327 -
327 +
328 this.global.sendInputEvents([event]);
329 }
330
331 function createClient(config) {
332 - return new RdpClient(config);
332 + return new RdpClient(config);
333 };
334
335 /**
@@ -338,28 +338,28 @@ function createClient(config) {
338 * @param socket {net.Socket}
339 */
340 function RdpServer(config, socket) {
341 - if (!(config.key && config.cert)) {
342 - throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools')
343 - }
344 - this.connected = false;
345 - this.bufferLayer = new layer.BufferLayer(socket);
346 - this.tpkt = new TPKT(this.bufferLayer);
347 - this.x224 = new x224.Server(this.tpkt, config.key, config.cert);
348 - this.mcs = new t125.mcs.Server(this.x224);
341 + if (!(config.key && config.cert)) {
342 + throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools')
343 + }
344 + this.connected = false;
345 + this.bufferLayer = new layer.BufferLayer(socket);
346 + this.tpkt = new TPKT(this.bufferLayer);
347 + this.x224 = new x224.Server(this.tpkt, config.key, config.cert);
348 + this.mcs = new t125.mcs.Server(this.x224);
349 };
350
351 inherits(RdpServer, events.EventEmitter);
352
353 -function createServer (config, next) {
354 - return net.createServer(function (socket) {
355 - next(new RdpServer(config, socket));
356 - });
353 +function createServer(config, next) {
354 + return net.createServer(function (socket) {
355 + next(new RdpServer(config, socket));
356 + });
357 };
358
359 /**
360 * Module exports
361 */
362 module.exports = {
363 - createClient : createClient,
364 - createServer : createServer
363 + createClient: createClient,
364 + createServer: createServer
365 };
\ No newline at end of file
views/default.handlebars
+16 -5
@@ -1379,6 +1379,10 @@
1379 <input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)" />
1380 <input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)" />
1381 <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)" /></div>
1382 + <div id="idx_dlgMoreButtons">
1383 + <a href=# id=morexxx1 style=cursor:pointer;color:gray;text-decoration:none title="Toggle advanced options" onclick=MoreToggle(true)>&#x25BC;</a>
1384 + <a href=# id=morexxx2 style=cursor:pointer;color:gray;text-decoration:none title="Toggle advanced options" onclick=MoreToggle(false)>&#x25B2;</a>
1385 + </div>
1386 </div>
1387 </div>
1388 <iframe name="fileUploadFrame" style="display:none"></iframe>
@@ -8552,7 +8556,11 @@
8556 x += addHtmlValue("Username", '<input type=text id=d2user style=width:230px onKeyUp=askRdpCredentialsValidate() maxlength=128 />');
8557 x += addHtmlValue("Password", '<input type=password id=d2pass style=width:230px maxlength=128 autocomplete=off />');
8558 if ((features2 & 0x00400000) == 0) { x += addHtmlValue("", '<label><input type="checkbox" id=d2savecred>' + "Remember credentials" + '</label>'); }
8555 - x + '</div></div>';
8559 + x += '</div>';
8560 + x += MoreStart();
8561 + x += addHtmlValue("Alt Shell", '<input type=text id=d2altshell style=width:230px maxlength=2048 />');
8562 + x += addHtmlValue("Working Dir", '<input type=text id=d2workdir style=width:230px maxlength=2048 />');
8563 + x += MoreEnd();
8564 setDialogMode(2, "RDP Credentials", 3, askRdpCredentialsEx, x);
8565 }
8566
@@ -8586,11 +8594,11 @@
8594 height = Q('DeskParent').offsetHeight;
8595 }
8596 if ((currentNode.rdp == 1) && (Q('d2mode').value == 1)) {
8589 - connectDesktop(null, 4, { servercred: true, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F });
8597 + connectDesktop(null, 4, { servercred: true, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F, altshell: Q('d2altshell').value, workdir: Q('d2workdir').value });
8598 } else {
8599 var savecred = false;
8600 if ((features2 & 0x00400000) == 0) { savecred = Q('d2savecred').checked; }
8593 - connectDesktop(null, 4, { domain: Q('d2domain').value, username: Q('d2user').value, password: Q('d2pass').value, savecred: savecred, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F });
8601 + connectDesktop(null, 4, { domain: Q('d2domain').value, username: Q('d2user').value, password: Q('d2pass').value, savecred: savecred, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F, altshell: Q('d2altshell').value, workdir: Q('d2workdir').value });
8602 }
8603 }
8604
@@ -16857,6 +16865,7 @@
16865
16866 // Reset dialog size
16867 QS('dialog').width = QS('dialog').top = QS('dialog').left = QS('dialog').right = QS('dialog').bottom = null;
16868 + MoreToggle(false);
16869
16870 QE('idx_dlgOkButton', true);
16871 QV('idx_dlgOkButton', b & 1);
@@ -16869,6 +16878,7 @@
16878 for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
16879 QV('dialog', x);
16880 if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
16881 + QV('idx_dlgMoreButtons', (Q('morexxx3') != null));
16882 }
16883
16884 function dialogclose(x) {
@@ -17470,8 +17480,9 @@
17480 function AddButton(v, f) { return '<input type=button value="' + v + '" onclick="' + f + '" style=margin:4px>'; }
17481 function AddButton2(v, f) { return '<input type=button value="' + v + '" onclick="' + f + '">'; }
17482 function AddRefreshButton(f) { return '<input type=button name=refreshbtn value=Refresh onclick="refreshButtons(false);' + f + '" style=margin:4px ' + (refreshButtonsState==false?'disabled':'') + '>'; }
17473 - function MoreStart() { return '<a href=# style=cursor:pointer;color:blue id=morexxx1 onclick=QV("morexxx1",false);QV("morexxx2",true)>&#x25BC; ' + "More" + '</a><div id=morexxx2 style=display:none><br><hr>'; };
17474 - function MoreEnd() { return '<a href=# style=cursor:pointer;color:blue onclick=QV("morexxx2",false);QV("morexxx1",true)>&#x25B2; ' + "Less" + '</a></div>'; };
17483 + function MoreStart() { return '<div id=morexxx3 style=display:none><hr>'; };
17484 + function MoreEnd() { return '</div>'; };
17485 + function MoreToggle(v) { QV("morexxx1",!v); QV("morexxx2",v); QV("morexxx3",v); }
17486 function getSelectedOptions(sel) { var opts = [], opt; for (var i = 0, len = sel.options.length; i < len; i++) { opt = sel.options[i]; if (opt.selected) { opts.push(opt.value); } } return opts; }
17487 function getInstance(x, y) { for (var i in x) { if (x[i]['InstanceID'] == y) return x[i]; } return null; }
17488 function getItem(x, y, z) { for (var i in x) { if (x[i][y] == z) return x[i]; } return null; }