diff --git a/MeshCentralServer.njsproj b/MeshCentralServer.njsproj
index 0f0a2e52..f458c404 100644
--- a/MeshCentralServer.njsproj
+++ b/MeshCentralServer.njsproj
@@ -198,6 +198,7 @@
+
diff --git a/apprelays.js b/apprelays.js
index 8c75eb9f..3a8d561b 100644
--- a/apprelays.js
+++ b/apprelays.js
@@ -152,7 +152,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
obj.wsClient._socket.pause();
try {
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(); }
}
@@ -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
delete 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 () {
send(['rdp-close']);
}).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 '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': {
if (obj.limitedinput == true) { // Limit keyboard input
var ok = false, k = msg[1];
diff --git a/public/scripts/agent-rdp-0.0.1.js b/public/scripts/agent-rdp-0.0.1.js
index 698ee7e0..436d9aa8 100644
--- a/public/scripts/agent-rdp-0.0.1.js
+++ b/public/scripts/agent-rdp-0.0.1.js
@@ -83,6 +83,10 @@ var CreateRDPDesktop = function (canvasid) {
obj.Stop();
break;
}
+ case 'rdp-clipboard': {
+ console.log('clipboard', msg[1]);
+ break;
+ }
case 'ping': { obj.socket.send('["pong"]'); break; }
case 'pong': { break; }
}
@@ -99,7 +103,15 @@ var CreateRDPDesktop = function (canvasid) {
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
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) {
if (obj.State == newstate) return;
obj.State = newstate;
@@ -153,14 +165,14 @@ var CreateRDPDesktop = function (canvasid) {
}
obj.m.handleKeyUp = function (e) {
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]));
e.preventDefault();
return false;
}
obj.m.handleKeyDown = function (e) {
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]));
e.preventDefault();
return false;
diff --git a/rdp/protocol/pdu/cliprdr.js b/rdp/protocol/pdu/cliprdr.js
new file mode 100644
index 00000000..15e3b86f
--- /dev/null
+++ b/rdp/protocol/pdu/cliprdr.js
@@ -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
+}
diff --git a/rdp/protocol/pdu/data.js b/rdp/protocol/pdu/data.js
index 58b2fb42..7c79012d 100644
--- a/rdp/protocol/pdu/data.js
+++ b/rdp/protocol/pdu/data.js
@@ -1040,6 +1040,36 @@ function pdu(userId, pduMessage, 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
* @param opt {object} type option
@@ -1147,5 +1177,7 @@ module.exports = {
updateDataPDU : updateDataPDU,
dataPDU : dataPDU,
fastPathBitmapUpdateDataPDU : fastPathBitmapUpdateDataPDU,
- fastPathUpdatePDU : fastPathUpdatePDU
+ fastPathUpdatePDU: fastPathUpdatePDU,
+ clipPDU: clipPDU,
+ ClipPDUMsgType: ClipPDUMsgType
};
\ No newline at end of file
diff --git a/rdp/protocol/pdu/index.js b/rdp/protocol/pdu/index.js
index dd714709..6afbeccd 100644
--- a/rdp/protocol/pdu/index.js
+++ b/rdp/protocol/pdu/index.js
@@ -21,10 +21,12 @@ var lic = require('./lic');
var sec = require('./sec');
var global = require('./global');
var data = require('./data');
+var cliprdr = require('./cliprdr');
module.exports = {
- lic : lic,
- sec : sec,
- global : global,
- data : data
+ lic: lic,
+ sec: sec,
+ global: global,
+ data: data,
+ cliprdr: cliprdr
};
diff --git a/rdp/protocol/rdp.js b/rdp/protocol/rdp.js
index f9fac6f8..97cb25a6 100644
--- a/rdp/protocol/rdp.js
+++ b/rdp/protocol/rdp.js
@@ -87,6 +87,7 @@ function RdpClient(config) {
this.x224 = new x224.Client(this.tpkt, config);
this.mcs = new t125.mcs.Client(this.x224);
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);
// config log level
@@ -145,6 +146,9 @@ function RdpClient(config) {
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
}
+ this.cliprdr.on('clipboard', (content) => {
+ this.emit('clipboard', content)
+ });
//bind all events
var self = this;
@@ -328,6 +332,14 @@ RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizon
this.global.sendInputEvents([event]);
}
+/**
+ * Clipboard event
+ * @param data {String} content for clipboard
+ */
+RdpClient.prototype.setClipboardData = function (content) {
+ this.cliprdr.setClipboardData(content);
+}
+
function createClient(config) {
return new RdpClient(config);
};
diff --git a/rdp/protocol/t125/mcs.js b/rdp/protocol/t125/mcs.js
index 1077a2a8..338aae01 100644
--- a/rdp/protocol/t125/mcs.js
+++ b/rdp/protocol/t125/mcs.js
@@ -25,6 +25,7 @@ var error = require('../../core').error;
var gcc = require('./gcc');
var per = require('./per');
var asn1 = require('../../asn1');
+var cliprdr = require('../pdu/cliprdr');
var Message = {
MCS_TYPE_CONNECT_INITIAL : 0x65,
@@ -43,10 +44,33 @@ var DomainMCSPDU = {
};
var Channel = {
- MCS_GLOBAL_CHANNEL : 1003,
- MCS_USERCHANNEL_BASE : 1001
+ MCS_GLOBAL_CHANNEL: 1003,
+ 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
* @returns {asn1.univ.Sequence}
@@ -126,7 +150,10 @@ function MCS(transport, recvOpCode, sendOpCode) {
this.transport = transport;
this.recvOpCode = recvOpCode;
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) {
for(var i in this) {
if(callback(this[i])) return this[i];
@@ -207,8 +234,9 @@ function Client(transport) {
this.channelsConnected = 0;
// init gcc information
- this.clientCoreData = gcc.clientCoreData();
- this.clientNetworkData = gcc.clientNetworkData(new type.Component([]));
+ this.clientCoreData = gcc.clientCoreData();
+ // cliprdr channel
+ this.clientNetworkData = gcc.clientNetworkData(new type.Component([RdpdrChannelDef, CliprdrChannelDef, RdpsndChannelDef]));
this.clientSecurityData = gcc.clientSecurityData();
// must be readed from protocol
@@ -317,7 +345,8 @@ Client.prototype.recvChannelJoinConfirm = function(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');
}
diff --git a/rdp/security/md4.js b/rdp/security/md4.js
index 38c0ba9b..ae9ab1e4 100644
--- a/rdp/security/md4.js
+++ b/rdp/security/md4.js
@@ -121,7 +121,7 @@
} else if (message.length === undefined) {
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;
};
diff --git a/views/default.handlebars b/views/default.handlebars
index 64699de7..b62b6083 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -7566,7 +7566,7 @@
if ((navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
try {
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 (ex) { console.log(ex); }
}
@@ -8332,7 +8332,8 @@
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
QE('connectbutton1h', hwonline);
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
QE('DeskESC', (deskState == 3) && (desktop.contype != 4));
QV('DeskESC', browserfullscreen && inputAllowed);
@@ -8737,7 +8738,7 @@
try {
navigator.clipboard.readText().then(function(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;
}
}).catch(function(err) { });
@@ -9323,8 +9324,12 @@
function showDeskClipSet() {
if (desktop == null || desktop.State != 3) return;
- 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));
+ if (desktop.m.setClipboard) {
+ 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