mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-01-13 07:53:20 -05:00
Merge branch 'master' of https://github.com/silversword411/MeshCentral
This commit is contained in:
commit
56527b76e0
@ -198,6 +198,7 @@
|
|||||||
<Compile Include="rdp\protocol\index.js" />
|
<Compile Include="rdp\protocol\index.js" />
|
||||||
<Compile Include="rdp\protocol\nla.js" />
|
<Compile Include="rdp\protocol\nla.js" />
|
||||||
<Compile Include="rdp\protocol\pdu\caps.js" />
|
<Compile Include="rdp\protocol\pdu\caps.js" />
|
||||||
|
<Compile Include="rdp\protocol\pdu\cliprdr.js" />
|
||||||
<Compile Include="rdp\protocol\pdu\data.js" />
|
<Compile Include="rdp\protocol\pdu\data.js" />
|
||||||
<Compile Include="rdp\protocol\pdu\global.js" />
|
<Compile Include="rdp\protocol\pdu\global.js" />
|
||||||
<Compile Include="rdp\protocol\pdu\index.js" />
|
<Compile Include="rdp\protocol\pdu\index.js" />
|
||||||
|
@ -152,7 +152,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
|||||||
obj.wsClient._socket.pause();
|
obj.wsClient._socket.pause();
|
||||||
try {
|
try {
|
||||||
obj.relaySocket.write(data, function () {
|
obj.relaySocket.write(data, function () {
|
||||||
try { obj.wsClient._socket.resume(); } catch (ex) { console.log(ex); }
|
if (obj.wsClient && obj.wsClient._socket) { try { obj.wsClient._socket.resume(); } catch (ex) { console.log(ex); } }
|
||||||
});
|
});
|
||||||
} catch (ex) { console.log(ex); obj.close(); }
|
} catch (ex) { console.log(ex); obj.close(); }
|
||||||
}
|
}
|
||||||
@ -201,6 +201,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
|||||||
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
|
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
|
||||||
delete bitmap.data;
|
delete bitmap.data;
|
||||||
send(['rdp-bitmap', bitmap]); // Send the bitmap metadata seperately, without bitmap data.
|
send(['rdp-bitmap', bitmap]); // Send the bitmap metadata seperately, without bitmap data.
|
||||||
|
}).on('clipboard', function (content) {
|
||||||
|
// Clipboard data changed
|
||||||
|
console.log('RDP clipboard recv', content);
|
||||||
|
send(['rdp-clipboard', content]);
|
||||||
}).on('close', function () {
|
}).on('close', function () {
|
||||||
send(['rdp-close']);
|
send(['rdp-close']);
|
||||||
}).on('error', function (err) {
|
}).on('error', function (err) {
|
||||||
@ -317,6 +321,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
|||||||
}
|
}
|
||||||
case 'mouse': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendPointerEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
|
case 'mouse': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendPointerEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
|
||||||
case 'wheel': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendWheelEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
|
case 'wheel': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendWheelEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
|
||||||
|
case 'clipboard': { rdpClient.setClipboardData(msg[1]); break; }
|
||||||
case 'scancode': {
|
case 'scancode': {
|
||||||
if (obj.limitedinput == true) { // Limit keyboard input
|
if (obj.limitedinput == true) { // Limit keyboard input
|
||||||
var ok = false, k = msg[1];
|
var ok = false, k = msg[1];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "1.0.21",
|
"version": "1.0.22",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Device Management",
|
"Remote Device Management",
|
||||||
"Remote Device Monitoring",
|
"Remote Device Monitoring",
|
||||||
|
@ -83,6 +83,10 @@ var CreateRDPDesktop = function (canvasid) {
|
|||||||
obj.Stop();
|
obj.Stop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'rdp-clipboard': {
|
||||||
|
console.log('clipboard', msg[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'ping': { obj.socket.send('["pong"]'); break; }
|
case 'ping': { obj.socket.send('["pong"]'); break; }
|
||||||
case 'pong': { break; }
|
case 'pong': { break; }
|
||||||
}
|
}
|
||||||
@ -99,7 +103,15 @@ var CreateRDPDesktop = function (canvasid) {
|
|||||||
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
|
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
|
||||||
if (obj.socket) { obj.socket.close(); }
|
if (obj.socket) { obj.socket.close(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj.m.setClipboard = function (content) {
|
||||||
|
console.log('s1');
|
||||||
|
if (obj.socket) {
|
||||||
|
console.log('s2', content);
|
||||||
|
obj.socket.send(JSON.stringify(['clipboard', content]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function changeState(newstate) {
|
function changeState(newstate) {
|
||||||
if (obj.State == newstate) return;
|
if (obj.State == newstate) return;
|
||||||
obj.State = newstate;
|
obj.State = newstate;
|
||||||
@ -153,14 +165,14 @@ var CreateRDPDesktop = function (canvasid) {
|
|||||||
}
|
}
|
||||||
obj.m.handleKeyUp = function (e) {
|
obj.m.handleKeyUp = function (e) {
|
||||||
if (!obj.socket || (obj.State != 3)) return;
|
if (!obj.socket || (obj.State != 3)) return;
|
||||||
console.log('handleKeyUp', Mstsc.scancode(e));
|
//console.log('handleKeyUp', Mstsc.scancode(e));
|
||||||
obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), false]));
|
obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), false]));
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
obj.m.handleKeyDown = function (e) {
|
obj.m.handleKeyDown = function (e) {
|
||||||
if (!obj.socket || (obj.State != 3)) return;
|
if (!obj.socket || (obj.State != 3)) return;
|
||||||
console.log('handleKeyDown', Mstsc.scancode(e));
|
//console.log('handleKeyDown', Mstsc.scancode(e));
|
||||||
obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), true]));
|
obj.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), true]));
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
|
327
rdp/protocol/pdu/cliprdr.js
Normal file
327
rdp/protocol/pdu/cliprdr.js
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
const type = require('../../core').type;
|
||||||
|
const EventEmitter = require('events').EventEmitter;
|
||||||
|
const caps = require('./caps');
|
||||||
|
const log = require('../../core').log;
|
||||||
|
const data = require('./data');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cliprdr channel for all clipboard
|
||||||
|
* capabilities exchange
|
||||||
|
*/
|
||||||
|
class Cliprdr extends EventEmitter {
|
||||||
|
|
||||||
|
constructor(transport) {
|
||||||
|
super();
|
||||||
|
this.transport = transport;
|
||||||
|
// must be init via connect event
|
||||||
|
this.userId = 0;
|
||||||
|
this.serverCapabilities = [];
|
||||||
|
this.clientCapabilities = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client side of Cliprdr channel automata
|
||||||
|
* @param transport
|
||||||
|
*/
|
||||||
|
class Client extends Cliprdr {
|
||||||
|
|
||||||
|
constructor(transport, fastPathTransport) {
|
||||||
|
|
||||||
|
super(transport, fastPathTransport);
|
||||||
|
|
||||||
|
this.transport.once('connect', (gccCore, userId, channelId) => {
|
||||||
|
this.connect(gccCore, userId, channelId);
|
||||||
|
}).on('close', () => {
|
||||||
|
this.emit('close');
|
||||||
|
}).on('error', (err) => {
|
||||||
|
this.emit('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.content = '';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* connect function
|
||||||
|
* @param gccCore {type.Component(clientCoreData)}
|
||||||
|
*/
|
||||||
|
connect(gccCore, userId, channelId) {
|
||||||
|
this.gccCore = gccCore;
|
||||||
|
this.userId = userId;
|
||||||
|
this.channelId = channelId;
|
||||||
|
this.transport.once('cliprdr', (s) => {
|
||||||
|
this.recv(s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
send(message) {
|
||||||
|
this.transport.send('cliprdr', new type.Component([
|
||||||
|
// Channel PDU Header
|
||||||
|
new type.UInt32Le(message.size()),
|
||||||
|
// CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST | CHANNEL_FLAG_SHOW_PROTOCOL
|
||||||
|
new type.UInt32Le(0x13),
|
||||||
|
message
|
||||||
|
]));
|
||||||
|
};
|
||||||
|
|
||||||
|
recv(s) {
|
||||||
|
s.offset = 18;
|
||||||
|
const pdu = data.clipPDU().read(s), type = data.ClipPDUMsgType;
|
||||||
|
|
||||||
|
switch (pdu.obj.header.obj.msgType.value) {
|
||||||
|
case type.CB_MONITOR_READY:
|
||||||
|
this.recvMonitorReadyPDU(s);
|
||||||
|
break;
|
||||||
|
case type.CB_FORMAT_LIST:
|
||||||
|
this.recvFormatListPDU(s);
|
||||||
|
break;
|
||||||
|
case type.CB_FORMAT_LIST_RESPONSE:
|
||||||
|
this.recvFormatListResponsePDU(s);
|
||||||
|
break;
|
||||||
|
case type.CB_FORMAT_DATA_REQUEST:
|
||||||
|
this.recvFormatDataRequestPDU(s);
|
||||||
|
break;
|
||||||
|
case type.CB_FORMAT_DATA_RESPONSE:
|
||||||
|
this.recvFormatDataResponsePDU(s);
|
||||||
|
break;
|
||||||
|
case type.CB_TEMP_DIRECTORY:
|
||||||
|
break;
|
||||||
|
case type.CB_CLIP_CAPS:
|
||||||
|
this.recvClipboardCapsPDU(s);
|
||||||
|
break;
|
||||||
|
case type.CB_FILECONTENTS_REQUEST:
|
||||||
|
}
|
||||||
|
|
||||||
|
this.transport.once('cliprdr', (s) => {
|
||||||
|
this.recv(s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive capabilities from server
|
||||||
|
* @param s {type.Stream}
|
||||||
|
*/
|
||||||
|
recvClipboardCapsPDU(s) {
|
||||||
|
// Start at 18
|
||||||
|
s.offset = 18;
|
||||||
|
// const pdu = data.clipPDU().read(s);
|
||||||
|
// console.log('recvClipboardCapsPDU', s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive monitor ready from server
|
||||||
|
* @param s {type.Stream}
|
||||||
|
*/
|
||||||
|
recvMonitorReadyPDU(s) {
|
||||||
|
s.offset = 18;
|
||||||
|
// const pdu = data.clipPDU().read(s);
|
||||||
|
// console.log('recvMonitorReadyPDU', s);
|
||||||
|
|
||||||
|
this.sendClipboardCapsPDU();
|
||||||
|
// this.sendClientTemporaryDirectoryPDU();
|
||||||
|
this.sendFormatListPDU();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send clipboard capabilities PDU
|
||||||
|
*/
|
||||||
|
sendClipboardCapsPDU() {
|
||||||
|
this.send(new type.Component({
|
||||||
|
msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_CLIP_CAPS),
|
||||||
|
msgFlags: new type.UInt16Le(0x00),
|
||||||
|
dataLen: new type.UInt32Le(0x10),
|
||||||
|
cCapabilitiesSets: new type.UInt16Le(0x01),
|
||||||
|
pad1: new type.UInt16Le(0x00),
|
||||||
|
capabilitySetType: new type.UInt16Le(0x01),
|
||||||
|
lengthCapability: new type.UInt16Le(0x0c),
|
||||||
|
version: new type.UInt32Le(0x02),
|
||||||
|
capabilityFlags: new type.UInt32Le(0x02)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send client temporary directory PDU
|
||||||
|
*/
|
||||||
|
sendClientTemporaryDirectoryPDU(path = '') {
|
||||||
|
// TODO
|
||||||
|
this.send(new type.Component({
|
||||||
|
msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_TEMP_DIRECTORY),
|
||||||
|
msgFlags: new type.UInt16Le(0x00),
|
||||||
|
dataLen: new type.UInt32Le(0x0208),
|
||||||
|
wszTempDir: new type.BinaryString(Buffer.from('D:\\Vectors' + Array(251).join('\x00'), 'ucs2'), { readLength : new type.CallableValue(520)})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send format list PDU
|
||||||
|
*/
|
||||||
|
sendFormatListPDU() {
|
||||||
|
this.send(new type.Component({
|
||||||
|
msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_LIST),
|
||||||
|
msgFlags: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
dataLen: new type.UInt32Le(0x24),
|
||||||
|
|
||||||
|
formatId6: new type.UInt32Le(0xc004),
|
||||||
|
formatName6: new type.BinaryString(Buffer.from('Native\x00' , 'ucs2'), { readLength : new type.CallableValue(14)}),
|
||||||
|
|
||||||
|
formatId8: new type.UInt32Le(0x0d),
|
||||||
|
formatName8: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
formatId9: new type.UInt32Le(0x10),
|
||||||
|
formatName9: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
formatId0: new type.UInt32Le(0x01),
|
||||||
|
formatName0: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
// dataLen: new type.UInt32Le(0xe0),
|
||||||
|
|
||||||
|
// formatId1: new type.UInt32Le(0xc08a),
|
||||||
|
// formatName1: new type.BinaryString(Buffer.from('Rich Text Format\x00' , 'ucs2'), { readLength : new type.CallableValue(34)}),
|
||||||
|
|
||||||
|
// formatId2: new type.UInt32Le(0xc145),
|
||||||
|
// formatName2: new type.BinaryString(Buffer.from('Rich Text Format Without Objects\x00' , 'ucs2'), { readLength : new type.CallableValue(66)}),
|
||||||
|
|
||||||
|
// formatId3: new type.UInt32Le(0xc143),
|
||||||
|
// formatName3: new type.BinaryString(Buffer.from('RTF As Text\x00' , 'ucs2'), { readLength : new type.CallableValue(24)}),
|
||||||
|
|
||||||
|
// formatId4: new type.UInt32Le(0x01),
|
||||||
|
// formatName4: new type.BinaryString(0x00),
|
||||||
|
|
||||||
|
formatId5: new type.UInt32Le(0x07),
|
||||||
|
formatName5: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
// formatId6: new type.UInt32Le(0xc004),
|
||||||
|
// formatName6: new type.BinaryString(Buffer.from('Native\x00' , 'ucs2'), { readLength : new type.CallableValue(14)}),
|
||||||
|
|
||||||
|
// formatId7: new type.UInt32Le(0xc00e),
|
||||||
|
// formatName7: new type.BinaryString(Buffer.from('Object Descriptor\x00' , 'ucs2'), { readLength : new type.CallableValue(36)}),
|
||||||
|
|
||||||
|
// formatId8: new type.UInt32Le(0x03),
|
||||||
|
// formatName8: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
// formatId9: new type.UInt32Le(0x10),
|
||||||
|
// formatName9: new type.UInt16Le(0x00),
|
||||||
|
|
||||||
|
// formatId0: new type.UInt32Le(0x07),
|
||||||
|
// formatName0: new type.UInt16Le(0x00),
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recvie format list PDU from server
|
||||||
|
* @param {type.Stream} s
|
||||||
|
*/
|
||||||
|
recvFormatListPDU(s) {
|
||||||
|
s.offset = 18;
|
||||||
|
// const pdu = data.clipPDU().read(s);
|
||||||
|
// console.log('recvFormatListPDU', s);
|
||||||
|
this.sendFormatListResponsePDU();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send format list reesponse
|
||||||
|
*/
|
||||||
|
sendFormatListResponsePDU() {
|
||||||
|
this.send(new type.Component({
|
||||||
|
msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_LIST_RESPONSE),
|
||||||
|
msgFlags: new type.UInt16Le(0x01),
|
||||||
|
dataLen: new type.UInt32Le(0x00),
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.sendFormatDataRequestPDU();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive format list response from server
|
||||||
|
* @param s {type.Stream}
|
||||||
|
*/
|
||||||
|
recvFormatListResponsePDU(s) {
|
||||||
|
s.offset = 18;
|
||||||
|
// const pdu = data.clipPDU().read(s);
|
||||||
|
// console.log('recvFormatListResponsePDU', s);
|
||||||
|
// this.sendFormatDataRequestPDU();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send format data request PDU
|
||||||
|
*/
|
||||||
|
sendFormatDataRequestPDU(formartId = 0x0d) {
|
||||||
|
this.send(new type.Component({
|
||||||
|
msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_DATA_REQUEST),
|
||||||
|
msgFlags: new type.UInt16Le(0x00),
|
||||||
|
dataLen: new type.UInt32Le(0x04),
|
||||||
|
requestedFormatId: new type.UInt32Le(formartId),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive format data request PDU from server
|
||||||
|
* @param s {type.Stream}
|
||||||
|
*/
|
||||||
|
recvFormatDataRequestPDU(s) {
|
||||||
|
s.offset = 18;
|
||||||
|
// const pdu = data.clipPDU().read(s);
|
||||||
|
// console.log('recvFormatDataRequestPDU', s);
|
||||||
|
this.sendFormatDataResponsePDU();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send format data reesponse PDU
|
||||||
|
*/
|
||||||
|
sendFormatDataResponsePDU() {
|
||||||
|
|
||||||
|
const bufs = Buffer.from(this.content + '\x00' , 'ucs2');
|
||||||
|
|
||||||
|
this.send(new type.Component({
|
||||||
|
msgType: new type.UInt16Le(data.ClipPDUMsgType.CB_FORMAT_DATA_RESPONSE),
|
||||||
|
msgFlags: new type.UInt16Le(0x01),
|
||||||
|
dataLen: new type.UInt32Le(bufs.length),
|
||||||
|
requestedFormatData: new type.BinaryString(bufs, { readLength : new type.CallableValue(bufs.length)})
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive format data response PDU from server
|
||||||
|
* @param s {type.Stream}
|
||||||
|
*/
|
||||||
|
recvFormatDataResponsePDU(s) {
|
||||||
|
s.offset = 18;
|
||||||
|
// const pdu = data.clipPDU().read(s);
|
||||||
|
const str = s.buffer.toString('ucs2', 26, s.buffer.length-2);
|
||||||
|
// console.log('recvFormatDataResponsePDU', str);
|
||||||
|
this.content = str;
|
||||||
|
this.emit('clipboard', str)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =====================================================================================
|
||||||
|
setClipboardData(content) {
|
||||||
|
this.content = content;
|
||||||
|
this.sendFormatListPDU();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Client
|
||||||
|
}
|
@ -1040,6 +1040,36 @@ function pdu(userId, pduMessage, opt) {
|
|||||||
return new type.Component(self, opt);
|
return new type.Component(self, opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ClipPDUMsgType = {
|
||||||
|
CB_MONITOR_READY: 0x0001,
|
||||||
|
CB_FORMAT_LIST: 0x0002,
|
||||||
|
CB_FORMAT_LIST_RESPONSE: 0x0003,
|
||||||
|
CB_FORMAT_DATA_REQUEST: 0x0004,
|
||||||
|
CB_FORMAT_DATA_RESPONSE: 0x0005,
|
||||||
|
CB_TEMP_DIRECTORY: 0x0006,
|
||||||
|
CB_CLIP_CAPS: 0x0007,
|
||||||
|
CB_FILECONTENTS_REQUEST: 0x0008
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {type.Component}
|
||||||
|
*/
|
||||||
|
function clipPDU() {
|
||||||
|
const self = {
|
||||||
|
header: new type.Factory(function (s) {
|
||||||
|
self.header = new type.Component({
|
||||||
|
msgType: new type.UInt16Le().read(s),
|
||||||
|
msgFlags: new type.UInt16Le().read(s),
|
||||||
|
dataLen: new type.UInt32Le().read(s)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
return new type.Component(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see http://msdn.microsoft.com/en-us/library/dd306368.aspx
|
* @see http://msdn.microsoft.com/en-us/library/dd306368.aspx
|
||||||
* @param opt {object} type option
|
* @param opt {object} type option
|
||||||
@ -1147,5 +1177,7 @@ module.exports = {
|
|||||||
updateDataPDU : updateDataPDU,
|
updateDataPDU : updateDataPDU,
|
||||||
dataPDU : dataPDU,
|
dataPDU : dataPDU,
|
||||||
fastPathBitmapUpdateDataPDU : fastPathBitmapUpdateDataPDU,
|
fastPathBitmapUpdateDataPDU : fastPathBitmapUpdateDataPDU,
|
||||||
fastPathUpdatePDU : fastPathUpdatePDU
|
fastPathUpdatePDU: fastPathUpdatePDU,
|
||||||
|
clipPDU: clipPDU,
|
||||||
|
ClipPDUMsgType: ClipPDUMsgType
|
||||||
};
|
};
|
@ -21,10 +21,12 @@ var lic = require('./lic');
|
|||||||
var sec = require('./sec');
|
var sec = require('./sec');
|
||||||
var global = require('./global');
|
var global = require('./global');
|
||||||
var data = require('./data');
|
var data = require('./data');
|
||||||
|
var cliprdr = require('./cliprdr');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
lic : lic,
|
lic: lic,
|
||||||
sec : sec,
|
sec: sec,
|
||||||
global : global,
|
global: global,
|
||||||
data : data
|
data: data,
|
||||||
|
cliprdr: cliprdr
|
||||||
};
|
};
|
||||||
|
@ -87,6 +87,7 @@ function RdpClient(config) {
|
|||||||
this.x224 = new x224.Client(this.tpkt, config);
|
this.x224 = new x224.Client(this.tpkt, config);
|
||||||
this.mcs = new t125.mcs.Client(this.x224);
|
this.mcs = new t125.mcs.Client(this.x224);
|
||||||
this.sec = new pdu.sec.Client(this.mcs, this.tpkt);
|
this.sec = new pdu.sec.Client(this.mcs, this.tpkt);
|
||||||
|
this.cliprdr = new pdu.cliprdr.Client(this.mcs);
|
||||||
this.global = new pdu.global.Client(this.sec, this.sec);
|
this.global = new pdu.global.Client(this.sec, this.sec);
|
||||||
|
|
||||||
// config log level
|
// config log level
|
||||||
@ -145,6 +146,9 @@ function RdpClient(config) {
|
|||||||
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
|
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.cliprdr.on('clipboard', (content) => {
|
||||||
|
this.emit('clipboard', content)
|
||||||
|
});
|
||||||
|
|
||||||
//bind all events
|
//bind all events
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -328,6 +332,14 @@ RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizon
|
|||||||
this.global.sendInputEvents([event]);
|
this.global.sendInputEvents([event]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clipboard event
|
||||||
|
* @param data {String} content for clipboard
|
||||||
|
*/
|
||||||
|
RdpClient.prototype.setClipboardData = function (content) {
|
||||||
|
this.cliprdr.setClipboardData(content);
|
||||||
|
}
|
||||||
|
|
||||||
function createClient(config) {
|
function createClient(config) {
|
||||||
return new RdpClient(config);
|
return new RdpClient(config);
|
||||||
};
|
};
|
||||||
|
@ -25,6 +25,7 @@ var error = require('../../core').error;
|
|||||||
var gcc = require('./gcc');
|
var gcc = require('./gcc');
|
||||||
var per = require('./per');
|
var per = require('./per');
|
||||||
var asn1 = require('../../asn1');
|
var asn1 = require('../../asn1');
|
||||||
|
var cliprdr = require('../pdu/cliprdr');
|
||||||
|
|
||||||
var Message = {
|
var Message = {
|
||||||
MCS_TYPE_CONNECT_INITIAL : 0x65,
|
MCS_TYPE_CONNECT_INITIAL : 0x65,
|
||||||
@ -43,10 +44,33 @@ var DomainMCSPDU = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var Channel = {
|
var Channel = {
|
||||||
MCS_GLOBAL_CHANNEL : 1003,
|
MCS_GLOBAL_CHANNEL: 1003,
|
||||||
MCS_USERCHANNEL_BASE : 1001
|
MCS_USERCHANNEL_BASE: 1001,
|
||||||
|
MCS_CLIPRDR_CHANNEL: 1005
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel Definde
|
||||||
|
*/
|
||||||
|
const RdpdrChannelDef = new type.Component({
|
||||||
|
name: new type.BinaryString(Buffer.from('rdpdr' + '\x00\x00\x00', 'binary'), { readLength: new type.CallableValue(8) }),
|
||||||
|
options: new type.UInt32Le(0x80800000)
|
||||||
|
});
|
||||||
|
|
||||||
|
const RdpsndChannelDef = new type.Component({
|
||||||
|
name: new type.BinaryString(Buffer.from('rdpsnd' + '\x00\x00', 'binary'), { readLength: new type.CallableValue(8) }),
|
||||||
|
options: new type.UInt32Le(0xc0000000)
|
||||||
|
});
|
||||||
|
|
||||||
|
const CliprdrChannelDef = new type.Component({
|
||||||
|
name: new type.BinaryString(Buffer.from('cliprdr' + '\x00', 'binary'), { readLength: new type.CallableValue(8) }),
|
||||||
|
// CHANNEL_OPTION_INITIALIZED |
|
||||||
|
// CHANNEL_OPTION_ENCRYPT_RDP |
|
||||||
|
// CHANNEL_OPTION_COMPRESS_RDP |
|
||||||
|
// CHANNEL_OPTION_SHOW_PROTOCOL
|
||||||
|
options: new type.UInt32Le(0xc0a00000)
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||||
* @returns {asn1.univ.Sequence}
|
* @returns {asn1.univ.Sequence}
|
||||||
@ -126,7 +150,10 @@ function MCS(transport, recvOpCode, sendOpCode) {
|
|||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.recvOpCode = recvOpCode;
|
this.recvOpCode = recvOpCode;
|
||||||
this.sendOpCode = sendOpCode;
|
this.sendOpCode = sendOpCode;
|
||||||
this.channels = [{id : Channel.MCS_GLOBAL_CHANNEL, name : 'global'}];
|
this.channels = [
|
||||||
|
{ id: Channel.MCS_GLOBAL_CHANNEL, name: 'global' },
|
||||||
|
{ id: Channel.MCS_CLIPRDR_CHANNEL, name: 'cliprdr' }
|
||||||
|
];
|
||||||
this.channels.find = function(callback) {
|
this.channels.find = function(callback) {
|
||||||
for(var i in this) {
|
for(var i in this) {
|
||||||
if(callback(this[i])) return this[i];
|
if(callback(this[i])) return this[i];
|
||||||
@ -207,8 +234,9 @@ function Client(transport) {
|
|||||||
this.channelsConnected = 0;
|
this.channelsConnected = 0;
|
||||||
|
|
||||||
// init gcc information
|
// init gcc information
|
||||||
this.clientCoreData = gcc.clientCoreData();
|
this.clientCoreData = gcc.clientCoreData();
|
||||||
this.clientNetworkData = gcc.clientNetworkData(new type.Component([]));
|
// cliprdr channel
|
||||||
|
this.clientNetworkData = gcc.clientNetworkData(new type.Component([RdpdrChannelDef, CliprdrChannelDef, RdpsndChannelDef]));
|
||||||
this.clientSecurityData = gcc.clientSecurityData();
|
this.clientSecurityData = gcc.clientSecurityData();
|
||||||
|
|
||||||
// must be readed from protocol
|
// must be readed from protocol
|
||||||
@ -317,7 +345,8 @@ Client.prototype.recvChannelJoinConfirm = function(s) {
|
|||||||
|
|
||||||
var channelId = per.readInteger16(s);
|
var channelId = per.readInteger16(s);
|
||||||
|
|
||||||
if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
|
//if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
|
||||||
|
if ((confirm !== 0) && (channelId === Channel.MCS_CLIPRDR_CHANNEL || channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
|
||||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
|
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
} else if (message.length === undefined) {
|
} else if (message.length === undefined) {
|
||||||
return method(message);
|
return method(message);
|
||||||
}
|
}
|
||||||
return crypto.createHash('md4').update(new Buffer(message)).digest('hex');
|
return crypto.createHash('md4').update(Buffer.from(message)).digest('hex');
|
||||||
};
|
};
|
||||||
return nodeMethod;
|
return nodeMethod;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -919,7 +919,8 @@
|
|||||||
<div id="terminalCustomUpperRight" style="float:left;margin-right:6px"></div>
|
<div id="terminalCustomUpperRight" style="float:left;margin-right:6px"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span id="connectbutton2span" style="margin-left:3px"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" style="height:28px" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
<span id="connectbutton2span" style="margin-left:3px"><input type="button" id="connectbutton2" value="Connect" style="height:28px" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
||||||
|
<span id="connectbutton2sspan" style="margin-right:4px"><input type="button" id="connectbutton2s" value="SSH Connect" style="height:28px" onclick=connectTerminal(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
||||||
<span id="disconnectbutton2span" style="margin-left:3px"><input type="button" id="disconnectbutton2" value="Disconnect" style="height:28px" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
|
<span id="disconnectbutton2span" style="margin-left:3px"><input type="button" id="disconnectbutton2" value="Disconnect" style="height:28px" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
|
||||||
<span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
|
<span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
|
||||||
</div>
|
</div>
|
||||||
@ -951,7 +952,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="margin-left:2px">
|
<div style="margin-left:2px">
|
||||||
<input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) onkeypress="return false" onkeydown="return false" type="button" style="display:none">
|
<input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) onkeypress="return false" onkeydown="return false" type="button" style="display:none">
|
||||||
<input id=p13Connect value="Connect" onclick=connectFiles(event) onkeypress="return false" onkeydown="return false" type="button">
|
<input id=p13Connect value="Connect" onclick=connectFiles(event,1) onkeypress="return false" onkeydown="return false" type="button" />
|
||||||
|
<input id=p13Connects value="SFTP Connect" onclick=connectFiles(event,2) onkeypress="return false" onkeydown="return false" type="button" />
|
||||||
|
<input id=p13Disconnect value="Disconnect" onclick=connectFiles(event) onkeypress="return false" onkeydown="return false" type="button" />
|
||||||
<span class=textOverGray id=p13Status>Disconnected</span>
|
<span class=textOverGray id=p13Status>Disconnected</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -4683,15 +4686,17 @@
|
|||||||
|
|
||||||
// Show the right buttons
|
// Show the right buttons
|
||||||
QV('disconnectbutton2span', (termState == true));
|
QV('disconnectbutton2span', (termState == true));
|
||||||
QV('connectbutton2span', (termState == false) && (currentNode.agent != null) && (currentNode.agent.caps & 2));
|
QV('connectbutton2span', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2) && (terminalNode.mtype != 3));
|
||||||
|
QV('connectbutton2sspan', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2) && (terminalNode.agent.id != 3) && (terminalNode.agent.id != 4));
|
||||||
|
|
||||||
// Enable buttons
|
// Enable buttons
|
||||||
var online = ((terminalNode.conn & 1) != 0) || (terminalNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
var online = ((terminalNode.conn & 1) != 0) || (terminalNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
||||||
QE('connectbutton2', online);
|
QE('connectbutton2', online);
|
||||||
|
QE('connectbutton2s', online);
|
||||||
|
|
||||||
// Enable action button if mesh type is not "local devices"
|
// Enable action button if mesh type is not "local devices"
|
||||||
QV('termActionsBtn', terminalNode.mtype != 3);
|
QV('termActionsBtn', terminalNode.mtype != 3);
|
||||||
if (terminalNode.mtype != 3) {
|
if (((termState == true) && (terminal.contype != 3)) || (terminalNode.agent.id == 3) || (terminalNode.agent.id == 4)) {
|
||||||
QH('terminalCustomUpperRight', '');
|
QH('terminalCustomUpperRight', '');
|
||||||
} else {
|
} else {
|
||||||
QH('terminalCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (terminalNode.sshport ? terminalNode.sshport : 22)) + '</a>');
|
QH('terminalCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (terminalNode.sshport ? terminalNode.sshport : 22)) + '</a>');
|
||||||
@ -4975,8 +4980,8 @@
|
|||||||
document.getElementsByClassName('xterm-viewport')[0].style.overflow = 'hidden';
|
document.getElementsByClassName('xterm-viewport')[0].style.overflow = 'hidden';
|
||||||
|
|
||||||
// Setup a terminal tunnel to the agent
|
// Setup a terminal tunnel to the agent
|
||||||
terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel((currentNode.mtype == 3) ? sshTunnelUpdate : tunnelUpdate, termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel((contype == 3) ? sshTunnelUpdate : tunnelUpdate, termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||||
if (currentNode.mtype == 3) { terminal.urlname = 'sshterminalrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
if (contype == 3) { terminal.urlname = 'sshterminalrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
||||||
terminal.debugmode = debugmode;
|
terminal.debugmode = debugmode;
|
||||||
terminal.m.debugmode = debugmode;
|
terminal.m.debugmode = debugmode;
|
||||||
terminal.options = termoptions;
|
terminal.options = termoptions;
|
||||||
@ -4984,7 +4989,7 @@
|
|||||||
if (termoptions.requireLogin) { terminal.options.requireLogin = true; }
|
if (termoptions.requireLogin) { terminal.options.requireLogin = true; }
|
||||||
terminal.Start(terminalNode._id);
|
terminal.Start(terminalNode._id);
|
||||||
terminal.onStateChanged = onTerminalStateChange;
|
terminal.onStateChanged = onTerminalStateChange;
|
||||||
terminal.contype = 1;
|
terminal.contype = contype;
|
||||||
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
||||||
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
||||||
} else {
|
} else {
|
||||||
@ -5028,15 +5033,24 @@
|
|||||||
filesNode = currentNode;
|
filesNode = currentNode;
|
||||||
var online = ((filesNode.conn & 1) != 0) || (filesNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
var online = ((filesNode.conn & 1) != 0) || (filesNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
||||||
QE('p13Connect', online);
|
QE('p13Connect', online);
|
||||||
|
QE('p13Connects', online);
|
||||||
|
QV('p13Connect', (files == null) && (filesNode.mtype == 2));
|
||||||
|
QV('p13Connects', (files == null) && (filesNode.agent != null) && (filesNode.agent.id != 3) && (filesNode.agent.id != 4));
|
||||||
|
QV('p13Disconnect', files != null);
|
||||||
if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
|
if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
|
||||||
p13setActions();
|
p13setActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFilesStateChange(xfiles, state) {
|
function onFilesStateChange(xfiles, state) {
|
||||||
setSessionActivity();
|
setSessionActivity();
|
||||||
p13Connect.value = (state == 0) ? "Connect" : "Disconnect";
|
QV('p13Connect', (state == 0) && (filesNode.mtype == 2));
|
||||||
|
QV('p13Connects', (state == 0) && (filesNode.agent != null) && (filesNode.agent.id != 3) && (filesNode.agent.id != 4));
|
||||||
|
QV('p13Disconnect', state != 0);
|
||||||
var str = StatusStrs[state];
|
var str = StatusStrs[state];
|
||||||
if (files.webRtcActive == true) { str += ", WebRTC"; }
|
if (state == 3) {
|
||||||
|
if (files.contype == 2) { str += ", SFTP"; }
|
||||||
|
if (files.webRtcActive == true) { str += ", WebRTC"; }
|
||||||
|
}
|
||||||
Q('p13Status').textContent = str;
|
Q('p13Status').textContent = str;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -5080,12 +5094,13 @@
|
|||||||
var autoConnectFilesTimer = null;
|
var autoConnectFilesTimer = null;
|
||||||
function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } }
|
function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } }
|
||||||
|
|
||||||
function connectFiles(e) {
|
function connectFiles(e, contype) {
|
||||||
p13clearConsoleMsg();
|
p13clearConsoleMsg();
|
||||||
if (!files) {
|
if (!files) {
|
||||||
// Setup a mesh agent files
|
// Setup a mesh agent files
|
||||||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||||
if (filesNode.mtype == 3) { files.urlname = 'sshfilesrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
if (contype == 2) { files.urlname = 'sshfilesrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
||||||
|
files.contype = contype;
|
||||||
files.attemptWebRTC = attemptWebRTC;
|
files.attemptWebRTC = attemptWebRTC;
|
||||||
files.onStateChanged = onFilesStateChange;
|
files.onStateChanged = onFilesStateChange;
|
||||||
files.onConsoleMessageChange = function () {
|
files.onConsoleMessageChange = function () {
|
||||||
@ -5304,7 +5319,8 @@
|
|||||||
QE('p13CopyButton', advancedFeatures && (cc > 0) && (cc == sfc) && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
QE('p13CopyButton', advancedFeatures && (cc > 0) && (cc == sfc) && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
||||||
QE('p13PasteButton', advancedFeatures && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
|
QE('p13PasteButton', advancedFeatures && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
|
||||||
}
|
}
|
||||||
if (filesNode.mtype != 3) {
|
var filesState = ((files != null) && (files.state != 0));
|
||||||
|
if (((filesState == true) && (files.contype != 2)) || (filesNode.agent.id == 3) || (filesNode.agent.id == 4)) {
|
||||||
QH('filesCustomUpperRight', '');
|
QH('filesCustomUpperRight', '');
|
||||||
} else {
|
} else {
|
||||||
QH('filesCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (filesNode.sshport ? filesNode.sshport : 22)) + '</a>');
|
QH('filesCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (filesNode.sshport ? filesNode.sshport : 22)) + '</a>');
|
||||||
|
@ -743,7 +743,7 @@
|
|||||||
<div id="terminalCustomUiButtons" style="float:left"></div>
|
<div id="terminalCustomUiButtons" style="float:left"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="button" id="autoconnectbutton2" value="AutoConnect" onclick=autoConnectTerminal(event) onkeypress="return false" onkeydown="return false" style="display:none" /><span id="connectbutton2span" style="margin-right:4px"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="connectbutton2hspan" style="margin-right:4px"><input type="button" id="connectbutton2h" value="HW Connect" title="Connect using Intel® AMT hardware KVM" onclick=connectTerminal(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="disconnectbutton2span" style="margin-right:4px"><input type="button" id="disconnectbutton2" value="Disconnect" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
|
<input type="button" id="autoconnectbutton2" value="AutoConnect" onclick=autoConnectTerminal(event) onkeypress="return false" onkeydown="return false" style="display:none" /><span id="connectbutton2span" style="margin-right:4px"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="connectbutton2sspan" style="margin-right:4px"><input type="button" id="connectbutton2s" cmenu=sshPortContextMenu value="SSH Connect" onclick=connectTerminal(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="connectbutton2hspan" style="margin-right:4px"><input type="button" id="connectbutton2h" value="HW Connect" title="Connect using Intel® AMT hardware KVM" onclick=connectTerminal(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="disconnectbutton2span" style="margin-right:4px"><input type="button" id="disconnectbutton2" value="Disconnect" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
|
||||||
<span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
|
<span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -801,7 +801,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) type="button" style="display:none" />
|
<input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) type="button" style="display:none" />
|
||||||
<input id=p13Connect value="Connect" onclick=connectFiles(event) type="button" />
|
<input id=p13Connect value="Connect" onclick=connectFiles(event,1) type="button" />
|
||||||
|
<input id=p13Connects value="SFTP Connect" cmenu=sshPortContextMenu onclick=connectFiles(event,2) type="button" />
|
||||||
|
<input id=p13Disconnect value="Disconnect" onclick=connectFiles(event) type="button" />
|
||||||
<span id=p13Status>Disconnected</span>
|
<span id=p13Status>Disconnected</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -2722,7 +2724,6 @@
|
|||||||
if (message.localport) { url += '&localport=' + message.localport; }
|
if (message.localport) { url += '&localport=' + message.localport; }
|
||||||
if (message.ip != null) { url += ('&remoteip=' + message.ip); }
|
if (message.ip != null) { url += ('&remoteip=' + message.ip); }
|
||||||
url += ('&appid=' + message.protocol + '&autoexit=1'); // Protocol: 0 = Custom, 1 = HTTP, 2 = HTTPS, 3 = RDP, 4 = PuTTY, 5 = WinSCP, 6 = MCRDesktop, 7 = MCRFiles
|
url += ('&appid=' + message.protocol + '&autoexit=1'); // Protocol: 0 = Custom, 1 = HTTP, 2 = HTTPS, 3 = RDP, 4 = PuTTY, 5 = WinSCP, 6 = MCRDesktop, 7 = MCRFiles
|
||||||
console.log(url);
|
|
||||||
downloadFile(url, '');
|
downloadFile(url, '');
|
||||||
} else if (message.tag == 'novnc') {
|
} else if (message.tag == 'novnc') {
|
||||||
var vncurl = window.location.origin + domainUrl + 'novnc/vnc.html?ws=wss%3A%2F%2F' + window.location.host + encodeURIComponentEx(domainUrl) + (message.localRelay?'local':'mesh') + 'relay.ashx%3Fauth%3D' + message.cookie + '&show_dot=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&l={{{lang}}}';
|
var vncurl = window.location.origin + domainUrl + 'novnc/vnc.html?ws=wss%3A%2F%2F' + window.location.host + encodeURIComponentEx(domainUrl) + (message.localRelay?'local':'mesh') + 'relay.ashx%3Fauth%3D' + message.cookie + '&show_dot=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&l={{{lang}}}';
|
||||||
@ -7565,7 +7566,7 @@
|
|||||||
if ((navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
|
if ((navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
|
||||||
try {
|
try {
|
||||||
navigator.clipboard.readText().then(function(text) {
|
navigator.clipboard.readText().then(function(text) {
|
||||||
meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
|
if (desktop.m.setClipboard) { desktop.m.setClipboard(text); } else { meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }
|
||||||
}).catch(function(err) { console.log(err); });
|
}).catch(function(err) { console.log(err); });
|
||||||
} catch (ex) { console.log(ex); }
|
} catch (ex) { console.log(ex); }
|
||||||
}
|
}
|
||||||
@ -8331,7 +8332,8 @@
|
|||||||
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
||||||
QE('connectbutton1h', hwonline);
|
QE('connectbutton1h', hwonline);
|
||||||
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
|
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
|
||||||
QE('DeskClip', (deskState == 3) && (desktop.contype != 4));
|
QE('DeskClip', deskState == 3);
|
||||||
|
//QE('DeskClip', (deskState == 3) && (desktop.contype != 4));
|
||||||
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
|
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
|
||||||
QE('DeskESC', (deskState == 3) && (desktop.contype != 4));
|
QE('DeskESC', (deskState == 3) && (desktop.contype != 4));
|
||||||
QV('DeskESC', browserfullscreen && inputAllowed);
|
QV('DeskESC', browserfullscreen && inputAllowed);
|
||||||
@ -8736,7 +8738,7 @@
|
|||||||
try {
|
try {
|
||||||
navigator.clipboard.readText().then(function(text) {
|
navigator.clipboard.readText().then(function(text) {
|
||||||
if ((text != null) && (deskLastClipboardSent != text)) {
|
if ((text != null) && (deskLastClipboardSent != text)) {
|
||||||
meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
|
if (desktop.m.setClipboard) { desktop.m.setClipboard(text); } else { meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }
|
||||||
deskLastClipboardSent = text;
|
deskLastClipboardSent = text;
|
||||||
}
|
}
|
||||||
}).catch(function(err) { });
|
}).catch(function(err) { });
|
||||||
@ -9322,8 +9324,12 @@
|
|||||||
|
|
||||||
function showDeskClipSet() {
|
function showDeskClipSet() {
|
||||||
if (desktop == null || desktop.State != 3) return;
|
if (desktop == null || desktop.State != 3) return;
|
||||||
meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
|
if (desktop.m.setClipboard) {
|
||||||
QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22) && (currentNode.agent.id != 34));
|
desktop.m.setClipboard(Q('d2clipText').value);
|
||||||
|
} else {
|
||||||
|
meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
|
||||||
|
QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22) && (currentNode.agent.id != 34));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send CTRL-ALT-DEL
|
// Send CTRL-ALT-DEL
|
||||||
@ -9557,7 +9563,8 @@
|
|||||||
|
|
||||||
// Show the right buttons
|
// Show the right buttons
|
||||||
QV('disconnectbutton2span', (termState == true));
|
QV('disconnectbutton2span', (termState == true));
|
||||||
QV('connectbutton2span', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2));
|
QV('connectbutton2span', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2) && (terminalNode.mtype != 3));
|
||||||
|
QV('connectbutton2sspan', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2) && (terminalNode.agent.id != 3) && (terminalNode.agent.id != 4));
|
||||||
if (terminalNode.mtype == 1) {
|
if (terminalNode.mtype == 1) {
|
||||||
QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != null) && (terminalNode.intelamt.state == 2));
|
QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != null) && (terminalNode.intelamt.state == 2));
|
||||||
QV('terminalSizeDropDown', (termState == false) && (terminalNode.intelamt != null) && (terminalNode.intelamt.state == 2));
|
QV('terminalSizeDropDown', (termState == false) && (terminalNode.intelamt != null) && (terminalNode.intelamt.state == 2));
|
||||||
@ -9568,7 +9575,7 @@
|
|||||||
|
|
||||||
// Enable action button if mesh type is not "local devices"
|
// Enable action button if mesh type is not "local devices"
|
||||||
QV('termActionsBtn', terminalNode.mtype != 3);
|
QV('termActionsBtn', terminalNode.mtype != 3);
|
||||||
if (terminalNode.mtype != 3) {
|
if (((termState == true) && (terminal.contype != 3)) || (terminalNode.agent.id == 3) || (terminalNode.agent.id == 4)) {
|
||||||
QH('terminalCustomUpperRight', '');
|
QH('terminalCustomUpperRight', '');
|
||||||
} else {
|
} else {
|
||||||
QH('terminalCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (terminalNode.sshport?terminalNode.sshport:22)) + '</a>');
|
QH('terminalCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (terminalNode.sshport?terminalNode.sshport:22)) + '</a>');
|
||||||
@ -9577,6 +9584,7 @@
|
|||||||
// Enable buttons
|
// Enable buttons
|
||||||
var online = ((terminalNode.conn & 1) != 0) || (terminalNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
var online = ((terminalNode.conn & 1) != 0) || (terminalNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
||||||
QE('connectbutton2', online);
|
QE('connectbutton2', online);
|
||||||
|
QE('connectbutton2s', online);
|
||||||
var hwonline = ((terminalNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
var hwonline = ((terminalNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
||||||
QE('connectbutton2h', hwonline);
|
QE('connectbutton2h', hwonline);
|
||||||
|
|
||||||
@ -9612,7 +9620,10 @@
|
|||||||
var xstate = state;
|
var xstate = state;
|
||||||
if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
|
if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
|
||||||
var str = StatusStrs[xstate];
|
var str = StatusStrs[xstate];
|
||||||
if (terminal.webRtcActive == true) { str += ", WebRTC"; }
|
if (xstate == 3) {
|
||||||
|
if (terminal.contype == 3) { str += ", SSH"; }
|
||||||
|
if (terminal.webRtcActive == true) { str += ", WebRTC"; }
|
||||||
|
}
|
||||||
QH('termstatus', str);
|
QH('termstatus', str);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -9777,6 +9788,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// contype: 1 = Agent, 2 = AMT, 3 = SSH
|
||||||
function connectTerminal(e, contype, options) {
|
function connectTerminal(e, contype, options) {
|
||||||
p12clearConsoleMsg();
|
p12clearConsoleMsg();
|
||||||
if (!terminal) {
|
if (!terminal) {
|
||||||
@ -9853,8 +9865,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Setup a terminal tunnel to the agent
|
// Setup a terminal tunnel to the agent
|
||||||
terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel((currentNode.mtype == 3)? sshTunnelUpdate : tunnelUpdate, termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel((contype == 3)? sshTunnelUpdate : tunnelUpdate, termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||||
if (currentNode.mtype == 3) { terminal.urlname = 'sshterminalrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
if (contype == 3) { terminal.urlname = 'sshterminalrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
||||||
terminal.debugmode = debugmode;
|
terminal.debugmode = debugmode;
|
||||||
terminal.m.debugmode = debugmode;
|
terminal.m.debugmode = debugmode;
|
||||||
terminal.options = termoptions;
|
terminal.options = termoptions;
|
||||||
@ -9862,7 +9874,7 @@
|
|||||||
if (termoptions.requireLogin) { terminal.options.requireLogin = true; }
|
if (termoptions.requireLogin) { terminal.options.requireLogin = true; }
|
||||||
terminal.Start(terminalNode._id);
|
terminal.Start(terminalNode._id);
|
||||||
terminal.onStateChanged = onTerminalStateChange;
|
terminal.onStateChanged = onTerminalStateChange;
|
||||||
terminal.contype = 1;
|
terminal.contype = contype;
|
||||||
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
||||||
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
||||||
} else {
|
} else {
|
||||||
@ -9880,7 +9892,7 @@
|
|||||||
terminal.onStateChanged = onTerminalStateChange;
|
terminal.onStateChanged = onTerminalStateChange;
|
||||||
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
||||||
terminal.Start(terminalNode._id);
|
terminal.Start(terminalNode._id);
|
||||||
terminal.contype = 1;
|
terminal.contype = contype;
|
||||||
terminal.m.terminalEmulation = 0;
|
terminal.m.terminalEmulation = 0;
|
||||||
terminal.m.fxEmulation = 0;
|
terminal.m.fxEmulation = 0;
|
||||||
Q('id_ttypebutton').value = terminalEmulations[0];
|
Q('id_ttypebutton').value = terminalEmulations[0];
|
||||||
@ -9980,14 +9992,23 @@
|
|||||||
filesNode = currentNode;
|
filesNode = currentNode;
|
||||||
var online = ((filesNode.conn & 1) != 0) || (filesNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
var online = ((filesNode.conn & 1) != 0) || (filesNode.mtype == 3); // If Agent (1) connected, enable Terminal
|
||||||
QE('p13Connect', online);
|
QE('p13Connect', online);
|
||||||
|
QE('p13Connects', online);
|
||||||
|
QV('p13Connect', (files == null) && (filesNode.mtype == 2));
|
||||||
|
QV('p13Connects', (files == null) && (filesNode.agent != null) && (filesNode.agent.id != 3) && (filesNode.agent.id != 4));
|
||||||
|
QV('p13Disconnect', files != null);
|
||||||
if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
|
if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
|
||||||
p13setActions();
|
p13setActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFilesStateChange(xfiles, state) {
|
function onFilesStateChange(xfiles, state) {
|
||||||
p13Connect.value = (state == 0) ? "Connect" : "Disconnect";
|
QV('p13Connect', (state == 0) && (files == null) && (filesNode.mtype == 2));
|
||||||
|
QV('p13Connects', (state == 0) && (files == null) && (filesNode.agent != null) && (filesNode.agent.id != 3) && (filesNode.agent.id != 4));
|
||||||
|
QV('p13Disconnect', state != 0);
|
||||||
var str = StatusStrs[state];
|
var str = StatusStrs[state];
|
||||||
if (files.webRtcActive == true) { str += ", WebRTC"; }
|
if (state == 3) {
|
||||||
|
if (files.contype == 2) { str += ", SFTP"; }
|
||||||
|
if (files.webRtcActive == true) { str += ", WebRTC"; }
|
||||||
|
}
|
||||||
Q('p13Status').textContent = str;
|
Q('p13Status').textContent = str;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -10033,12 +10054,14 @@
|
|||||||
var autoConnectFilesTimer = null;
|
var autoConnectFilesTimer = null;
|
||||||
function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } }
|
function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } }
|
||||||
|
|
||||||
function connectFiles(e) {
|
// 1 = Agent, 2 = SFTP
|
||||||
|
function connectFiles(e, contype) {
|
||||||
p13clearConsoleMsg();
|
p13clearConsoleMsg();
|
||||||
if (!files) {
|
if (!files) {
|
||||||
// Setup a mesh agent files
|
// Setup a mesh agent files
|
||||||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||||
if (filesNode.mtype == 3) { files.urlname = 'sshfilesrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
if (contype == 2) { files.urlname = 'sshfilesrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
|
||||||
|
files.contype = contype;
|
||||||
files.attemptWebRTC = attemptWebRTC;
|
files.attemptWebRTC = attemptWebRTC;
|
||||||
files.onStateChanged = onFilesStateChange;
|
files.onStateChanged = onFilesStateChange;
|
||||||
files.onConsoleMessageChange = function () {
|
files.onConsoleMessageChange = function () {
|
||||||
@ -10340,7 +10363,8 @@
|
|||||||
QE('p13ZipButton', advancedFeatures && (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
QE('p13ZipButton', advancedFeatures && (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
||||||
QE('p13PasteButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
|
QE('p13PasteButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
|
||||||
}
|
}
|
||||||
if (filesNode.mtype != 3) {
|
var filesState = ((files != null) && (files.state != 0));
|
||||||
|
if (((filesState == true) && (files.contype != 2)) || (filesNode.agent.id == 3) || (filesNode.agent.id == 4)) {
|
||||||
QH('filesCustomUpperRight', '');
|
QH('filesCustomUpperRight', '');
|
||||||
} else {
|
} else {
|
||||||
QH('filesCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (filesNode.sshport?filesNode.sshport:22)) + '</a>');
|
QH('filesCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (filesNode.sshport?filesNode.sshport:22)) + '</a>');
|
||||||
|
Loading…
Reference in New Issue
Block a user