mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-09-11 08:55:32 +03:00
Added recodings manager and recording improvements.
This commit is contained in:
parent
69736ca4da
commit
efc378be6b
@ -86,8 +86,10 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
obj.protocolOptions = null; // Set to the protocol options of the first viewer that connected.
|
||||
obj.viewerConnected = false; // Set to true if one viewer attempted to connect to the agent.
|
||||
obj.recordingFile = null; // Present if we are recording to file.
|
||||
obj.recordingFileSize = 0; // Current size of the recording file.
|
||||
obj.recordingFileWriting = false; // Set to true is we are in the process if writing to the recording file.
|
||||
obj.startTime = null; // Starting time of the multiplex session.
|
||||
obj.userIds = []; // List of userid's that have intertracted with this session.
|
||||
|
||||
// Add an agent or viewer
|
||||
obj.addPeer = function (peer) {
|
||||
@ -108,6 +110,9 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
peer.sendQueue = [];
|
||||
peer.paused = false;
|
||||
|
||||
// Add the user to the userids list if needed
|
||||
if ((peer.user != null) && (obj.userIds.indexOf(peer.user._id) == -1)) { obj.userIds.push(peer.user._id); }
|
||||
|
||||
// Setup slow relay is requested. This will show down sending any data to this viewer.
|
||||
if ((peer.req.query.slowrelay != null)) {
|
||||
var sr = null;
|
||||
@ -125,7 +130,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
// Log joining the multiplex session
|
||||
if (obj.startTime != null) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: peer.user._id, username: peer.user.name, msg: "Joined desktop multiplex session", protocol: 2 };
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, peer.user._id], obj, event); // TODO: Add Node MeshID to targets
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, peer.user._id, obj.meshid], obj, event);
|
||||
}
|
||||
} else {
|
||||
//console.log('addPeer-agent', obj.nodeid);
|
||||
@ -149,7 +154,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
// Log multiplex session start
|
||||
if ((obj.agent != null) && (obj.viewers.length > 0) && (obj.startTime == null)) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.viewers[0].user._id, username: obj.viewers[0].user.name, msg: "Started desktop multiplex session", protocol: 2 };
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, obj.viewers[0].user._id], obj, event); // TODO: Add Node MeshID to targets
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, obj.viewers[0].user._id, obj.meshid], obj, event);
|
||||
obj.startTime = Date.now();
|
||||
}
|
||||
return true;
|
||||
@ -195,7 +200,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
// Log leaving the multiplex session
|
||||
if (obj.startTime != null) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: peer.user._id, username: peer.user.name, msg: "Left the desktop multiplex session", protocol: 2 };
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, peer.user._id], obj, event); // TODO: Add Node MeshID to targets
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, peer.user._id, obj.meshid], obj, event);
|
||||
}
|
||||
|
||||
// If this is the last viewer, disconnect the agent
|
||||
@ -214,12 +219,26 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
|
||||
// Close the recording file if needed
|
||||
if (obj.recordingFile != null) {
|
||||
// Compute session length
|
||||
if (obj.startTime != null) { obj.sessionStart = obj.startTime; obj.sessionLength = Math.round((Date.now() - obj.startTime) / 1000); }
|
||||
|
||||
// Write the last record of the recording file
|
||||
var rf = obj.recordingFile;
|
||||
delete obj.recordingFile;
|
||||
recordingEntry(rf.fd, 3, 0, 'MeshCentralMCREC', function (fd, filename) {
|
||||
parent.parent.fs.close(fd);
|
||||
|
||||
// Now that the recording file is closed, check if we need to index this file.
|
||||
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', filename); }
|
||||
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.parent.path.basename(filename);
|
||||
var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: obj.nodeid, msg: "Finished recording session" + (obj.sessionLength ? (', ' + obj.sessionLength + ' second(s)') : ''), filename: basefile, size: obj.recordingFileSize, protocol: 2, icon: obj.icon, name: obj.name, meshid: obj.meshid, userids: obj.userIds, multiplex: true };
|
||||
var mesh = parent.meshes[obj.meshid];
|
||||
if (mesh != null) { event.meshname = mesh.name; }
|
||||
if (obj.sessionStart) { event.startTime = obj.sessionStart; event.lengthTime = obj.sessionLength; }
|
||||
parent.parent.DispatchEvent(['*', 'recording', obj.nodeid, obj.meshid], obj, event);
|
||||
|
||||
cleanUpRecordings();
|
||||
}, rf.filename);
|
||||
}
|
||||
@ -227,7 +246,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
// Log end of multiplex session
|
||||
if (obj.startTime != null) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, msg: "Closed desktop multiplex session" + ', ' + Math.floor((Date.now() - obj.startTime) / 1000) + ' second(s)', protocol: 2 };
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid], obj, event); // TODO: Add Node MeshID to targets
|
||||
parent.parent.DispatchEvent(['*', obj.nodeid, obj.meshid], obj, event);
|
||||
obj.startTime = null;
|
||||
}
|
||||
|
||||
@ -647,7 +666,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
}
|
||||
// Write the recording file header
|
||||
parent.parent.debug('relay', 'Relay: Started recoding to file: ' + recFullFilename);
|
||||
var metadata = { magic: 'MeshCentralRelaySession', ver: 1, nodeid: obj.nodeid, time: new Date().toLocaleString(), protocol: 2 };
|
||||
var metadata = { magic: 'MeshCentralRelaySession', ver: 1, nodeid: obj.nodeid, meshid: obj.meshid, time: new Date().toLocaleString(), protocol: 2, devicename: obj.name, devicegroup: obj.meshname };
|
||||
var firstBlock = JSON.stringify(metadata);
|
||||
recordingEntry(fd, 1, 0, firstBlock, function () {
|
||||
obj.recordingFile = { fd: fd, filename: recFullFilename };
|
||||
@ -684,6 +703,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, blockData]);
|
||||
parent.parent.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
|
||||
obj.recordingFileSize += block.length;
|
||||
} else {
|
||||
// Binary write
|
||||
var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
@ -693,6 +713,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, data]);
|
||||
parent.parent.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
|
||||
obj.recordingFileSize += block.length;
|
||||
}
|
||||
} catch (ex) { console.log(ex); func(fd, tag); }
|
||||
}
|
||||
@ -725,7 +746,14 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
||||
}
|
||||
}
|
||||
|
||||
recordingSetup(domain, function () { func(obj); });
|
||||
// Get node information
|
||||
parent.db.Get(nodeid, function (err, nodes) {
|
||||
if ((err != null) || (nodes.length != 1)) { func(null); }
|
||||
obj.meshid = nodes[0].meshid;
|
||||
obj.icon = nodes[0].icon;
|
||||
obj.name = nodes[0].name;
|
||||
recordingSetup(domain, function () { func(obj); });
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -784,7 +812,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Soft disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
|
||||
if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'DesktopRelay: Hard disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
|
||||
if (obj.relaySessionCounted) { parent.relaySessionCount--; delete obj.relaySessionCounted; }
|
||||
if (obj.deskDecoder != null) { if (obj.deskDecoder.removePeer(obj) == true) { delete parent.desktoprelays[obj.nodeid]; } }
|
||||
if (obj.deskMultiplexor != null) { if (obj.deskMultiplexor.removePeer(obj) == true) { delete parent.desktoprelays[obj.nodeid]; } }
|
||||
|
||||
// Aggressive cleanup
|
||||
delete obj.id;
|
||||
@ -793,7 +821,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
delete obj.user;
|
||||
delete obj.nodeid;
|
||||
delete obj.ruserid;
|
||||
delete obj.deskDecoder;
|
||||
delete obj.deskMultiplexor;
|
||||
|
||||
// Clear timers if present
|
||||
if (obj.pingtimer != null) { clearInterval(obj.pingtimer); delete obj.pingtimer; }
|
||||
@ -885,23 +913,30 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
}
|
||||
|
||||
// Create if needed and add this peer to the desktop multiplexor
|
||||
obj.deskDecoder = parent.desktoprelays[obj.nodeid];
|
||||
if (obj.deskDecoder == null) {
|
||||
obj.deskMultiplexor = parent.desktoprelays[obj.nodeid];
|
||||
if (obj.deskMultiplexor == null) {
|
||||
parent.desktoprelays[obj.nodeid] = 1; // Indicate that the creating of the desktop multiplexor is pending.
|
||||
parent.parent.debug('relay', 'DesktopRelay: Creating new desktop multiplexor');
|
||||
CreateDesktopMultiplexor(parent, domain, obj.nodeid, function (deskDecoder) {
|
||||
obj.deskDecoder = deskDecoder;
|
||||
parent.desktoprelays[obj.nodeid] = obj.deskDecoder;
|
||||
obj.deskDecoder.addPeer(obj);
|
||||
ws._socket.resume(); // Release the traffic
|
||||
CreateDesktopMultiplexor(parent, domain, obj.nodeid, function (deskMultiplexor) {
|
||||
if (deskMultiplexor != null) {
|
||||
// Desktop multiplexor was created, use it.
|
||||
obj.deskMultiplexor = deskMultiplexor;
|
||||
parent.desktoprelays[obj.nodeid] = obj.deskMultiplexor;
|
||||
obj.deskMultiplexor.addPeer(obj);
|
||||
ws._socket.resume(); // Release the traffic
|
||||
} else {
|
||||
// An error has occured, close this connection
|
||||
delete parent.desktoprelays[obj.nodeid];
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (obj.deskDecoder == 1) {
|
||||
if (obj.deskMultiplexor == 1) {
|
||||
// The multiplexor is being created, hold a little and try again. This is to prevent a possible race condition.
|
||||
setTimeout(function () { performRelay(++retryCount); }, 50);
|
||||
} else {
|
||||
// Hook up this peer to the multiplexor and release the traffic
|
||||
obj.deskDecoder.addPeer(obj);
|
||||
obj.deskMultiplexor.addPeer(obj);
|
||||
ws._socket.resume();
|
||||
}
|
||||
}
|
||||
@ -910,7 +945,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
// When data is received from the mesh relay web socket
|
||||
ws.on('message', function (data) {
|
||||
// If this data was received by the agent, decode it.
|
||||
if (this.me.deskDecoder != null) { this.me.deskDecoder.processData(this.me, data); }
|
||||
if (this.me.deskMultiplexor != null) { this.me.deskMultiplexor.processData(this.me, data); }
|
||||
});
|
||||
|
||||
// If error, close both sides of the relay.
|
||||
|
46
meshrelay.js
46
meshrelay.js
@ -222,8 +222,8 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
if ((sessionUser != null) && (domain.sessionrecording == true || ((typeof domain.sessionrecording == 'object') && ((domain.sessionrecording.protocols == null) || (domain.sessionrecording.protocols.indexOf(parseInt(obj.req.query.p)) >= 0))))) {
|
||||
// Get the computer name
|
||||
parent.db.Get(obj.req.query.nodeid, function (err, nodes) {
|
||||
var xusername = '', xdevicename = '', xdevicename2 = null;
|
||||
if ((nodes != null) && (nodes.length == 1)) { xdevicename2 = nodes[0].name; xdevicename = '-' + parent.common.makeFilename(nodes[0].name); }
|
||||
var xusername = '', xdevicename = '', xdevicename2 = null, node = null;
|
||||
if ((nodes != null) && (nodes.length == 1)) { node = nodes[0]; xdevicename2 = node.name; xdevicename = '-' + parent.common.makeFilename(node.name); }
|
||||
|
||||
// Get the username and make it acceptable as a filename
|
||||
if (sessionUser._id) { xusername = '-' + parent.common.makeFilename(sessionUser._id.split('/')[2]); }
|
||||
@ -250,8 +250,9 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
var metadata = { magic: 'MeshCentralRelaySession', ver: 1, userid: sessionUser._id, username: sessionUser.name, sessionid: obj.id, ipaddr1: cleanRemoteAddr(obj.req.ip), ipaddr2: cleanRemoteAddr(obj.peer.req.ip), time: new Date().toLocaleString(), protocol: (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.p), nodeid: (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.nodeid ) };
|
||||
if (xdevicename2 != null) { metadata.devicename = xdevicename2; }
|
||||
var firstBlock = JSON.stringify(metadata);
|
||||
recordingEntry(fd, 1, 0, firstBlock, function () {
|
||||
try { relayinfo.peer1.ws.logfile = ws.logfile = { fd: fd, lock: false, filename: recFullFilename }; } catch (ex) {
|
||||
var logfile = { fd: fd, lock: false, filename: recFullFilename, startTime: Date.now(), size: 0, nodeid: node._id, meshid: node.meshid, name: node.name, icon: node.icon };
|
||||
recordingEntry(logfile, 1, 0, firstBlock, function () {
|
||||
try { relayinfo.peer1.ws.logfile = ws.logfile = logfile; } catch (ex) {
|
||||
try { ws.send('c'); } catch (ex) { } // Send connect to both peers, 'cr' indicates the session is being recorded.
|
||||
try { relayinfo.peer1.ws.send('c'); } catch (ex) { }
|
||||
return;
|
||||
@ -323,7 +324,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
if (this.logfile != null) {
|
||||
// Write data to log file then perform relay
|
||||
var xthis = this;
|
||||
recordingEntry(this.logfile.fd, 2, ((obj.req.query.browser) ? 2 : 0), data, function () { xthis.peer.send(data, ws.flushSink); });
|
||||
recordingEntry(this.logfile, 2, ((obj.req.query.browser) ? 2 : 0), data, function () { xthis.peer.send(data, ws.flushSink); });
|
||||
} else {
|
||||
// Perform relay
|
||||
this.peer.send(data, ws.flushSink);
|
||||
@ -335,7 +336,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
if (this.logfile != null) {
|
||||
// Write data to log file then perform slow relay
|
||||
var xthis = this;
|
||||
recordingEntry(this.logfile.fd, 2, ((obj.req.query.browser) ? 2 : 0), data, function () {
|
||||
recordingEntry(this.logfile, 2, ((obj.req.query.browser) ? 2 : 0), data, function () {
|
||||
setTimeout(function () { xthis.peer.send(data, ws.flushSink); }, xthis.peer.slowRelay);
|
||||
});
|
||||
} else {
|
||||
@ -406,10 +407,29 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
var logfile = ws.logfile;
|
||||
delete ws.logfile;
|
||||
if (peer.ws) { delete peer.ws.logfile; }
|
||||
recordingEntry(logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, tag) {
|
||||
parent.parent.fs.close(fd);
|
||||
recordingEntry(logfile, 3, 0, 'MeshCentralMCREC', function (logfile, tag) {
|
||||
parent.parent.fs.close(logfile.fd);
|
||||
|
||||
// Now that the recording file is closed, check if we need to index this file.
|
||||
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); }
|
||||
|
||||
// Compute session length
|
||||
var sessionLength = null;
|
||||
if (tag.logfile.startTime != null) { sessionLength = Math.round((Date.now() - tag.logfile.startTime) / 1000); }
|
||||
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.parent.path.basename(tag.logfile.filename);
|
||||
var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: tag.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: tag.logfile.size };
|
||||
if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
|
||||
var xprotocol = (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.p);
|
||||
if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
|
||||
var mesh = parent.meshes[tag.logfile.meshid];
|
||||
if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
|
||||
if (tag.logfile.startTime) { event.startTime = tag.logfile.startTime; event.lengthTime = sessionLength; }
|
||||
if (tag.logfile.name) { event.name = tag.logfile.name; }
|
||||
if (tag.logfile.icon) { event.icon = tag.logfile.icon; }
|
||||
parent.parent.DispatchEvent(['*', 'recording', obj.nodeid, obj.meshid], obj, event);
|
||||
|
||||
cleanUpRecordings();
|
||||
}, { ws: ws, pws: peer.ws, logfile: logfile });
|
||||
}
|
||||
@ -428,7 +448,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
}
|
||||
|
||||
// Record a new entry in a recording log
|
||||
function recordingEntry(fd, type, flags, data, func, tag) {
|
||||
function recordingEntry(logfile, type, flags, data, func, tag) {
|
||||
try {
|
||||
if (typeof data == 'string') {
|
||||
// String write
|
||||
@ -438,7 +458,8 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
header.writeInt32BE(blockData.length, 4); // Size
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, blockData]);
|
||||
parent.parent.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
|
||||
parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
} else {
|
||||
// Binary write
|
||||
var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
@ -447,9 +468,10 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
header.writeInt32BE(data.length, 4); // Size
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, data]);
|
||||
parent.parent.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
|
||||
parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
}
|
||||
} catch (ex) { console.log(ex); func(fd, tag); }
|
||||
} catch (ex) { console.log(ex); func(logfile, tag); }
|
||||
}
|
||||
|
||||
// Mark this relay session as authenticated if this is the user end.
|
||||
|
31
meshuser.js
31
meshuser.js
@ -1211,6 +1211,37 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'recordings': {
|
||||
if (((user.siteadmin & SITERIGHT_RECORDINGS) == 0) || (domain.sessionrecording == null)) return; // Check if recordings is enabled and we have rights to do this.
|
||||
var recordingsPath = null;
|
||||
if (domain.sessionrecording.filepath) { recordingsPath = domain.sessionrecording.filepath; } else { recordingsPath = parent.parent.recordpath; }
|
||||
if (recordingsPath == null) return;
|
||||
fs.readdir(recordingsPath, function (err, files) {
|
||||
if (err != null) return;
|
||||
if ((command.limit == null) || (typeof command.limit != 'number')) {
|
||||
// Send the list of all recordings
|
||||
db.GetEvents(['recording'], domain.id, function (err, docs) {
|
||||
if (err != null) return;
|
||||
for (var i in docs) {
|
||||
delete docs[i].action; delete docs[i].etype; delete docs[i].msg; // TODO: We could make a more specific query in the DB and never have these.
|
||||
if (files.indexOf(docs[i].filename) >= 0) { docs[i].present = 1; }
|
||||
}
|
||||
try { ws.send(JSON.stringify({ action: 'recordings', events: docs, tag: command.tag })); } catch (ex) { }
|
||||
});
|
||||
} else {
|
||||
// Send the list of most recent recordings, up to 'limit' count
|
||||
db.GetEventsWithLimit(['recording'], domain.id, command.limit, function (err, docs) {
|
||||
if (err != null) return;
|
||||
for (var i in docs) {
|
||||
delete docs[i].action; delete docs[i].etype; delete docs[i].msg; // TODO: We could make a more specific query in the DB and never have these.
|
||||
if (files.indexOf(docs[i].filename) >= 0) { docs[i].present = 1; }
|
||||
}
|
||||
try { ws.send(JSON.stringify({ action: 'recordings', events: docs, tag: command.tag })); } catch (ex) { }
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'users':
|
||||
{
|
||||
// Request a list of all users
|
||||
|
@ -607,7 +607,7 @@
|
||||
"default-mobile.handlebars->9->248",
|
||||
"default-mobile.handlebars->9->77",
|
||||
"default.handlebars->25->1339",
|
||||
"default.handlebars->25->1649",
|
||||
"default.handlebars->25->1676",
|
||||
"default.handlebars->25->727"
|
||||
]
|
||||
},
|
||||
@ -2956,7 +2956,7 @@
|
||||
"ru": "Счетчик ошибок агента",
|
||||
"zh-chs": "座席錯誤計數器",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1659"
|
||||
"default.handlebars->25->1686"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -3025,7 +3025,7 @@
|
||||
"ru": "Сессии агентов",
|
||||
"zh-chs": "座席會議",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1675"
|
||||
"default.handlebars->25->1702"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -3148,7 +3148,7 @@
|
||||
"ru": "Агенты",
|
||||
"zh-chs": "代理商",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1688"
|
||||
"default.handlebars->25->1715"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -3970,7 +3970,7 @@
|
||||
"ru": "Вы уверенны, что {0} плагин: {1}",
|
||||
"zh-chs": "您確定要{0}插件嗎:{1}",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1728"
|
||||
"default.handlebars->25->1755"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4399,7 +4399,7 @@
|
||||
"ru": "Плохой ключ",
|
||||
"zh-chs": "錯誤的簽名",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1666"
|
||||
"default.handlebars->25->1693"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4416,7 +4416,7 @@
|
||||
"ru": "Плохой веб-сертификат",
|
||||
"zh-chs": "錯誤的網絡證書",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1665"
|
||||
"default.handlebars->25->1692"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4679,7 +4679,7 @@
|
||||
"ru": "CIRA Сервер",
|
||||
"zh-chs": "CIRA服務器",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1716"
|
||||
"default.handlebars->25->1743"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4696,7 +4696,7 @@
|
||||
"ru": "CIRA Сервер команды",
|
||||
"zh-chs": "CIRA服務器命令",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1717"
|
||||
"default.handlebars->25->1744"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4729,7 +4729,7 @@
|
||||
"ru": "Загрузка CPU",
|
||||
"zh-chs": "CPU負載",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1680"
|
||||
"default.handlebars->25->1707"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4746,7 +4746,7 @@
|
||||
"ru": "Загрузка CPU за последние 15 минут",
|
||||
"zh-chs": "最近15分鐘的CPU負載",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1683"
|
||||
"default.handlebars->25->1710"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4763,7 +4763,7 @@
|
||||
"ru": "Загрузка CPU за последние 5 минут",
|
||||
"zh-chs": "最近5分鐘的CPU負載",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1682"
|
||||
"default.handlebars->25->1709"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4780,7 +4780,7 @@
|
||||
"ru": "Загрузка CPU за последнюю минуту",
|
||||
"zh-chs": "最後一分鐘的CPU負載",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1681"
|
||||
"default.handlebars->25->1708"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -4835,7 +4835,7 @@
|
||||
"ru": "Ошибка вызова",
|
||||
"zh-chs": "通話錯誤",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1729"
|
||||
"default.handlebars->25->1756"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -5302,7 +5302,7 @@
|
||||
"ru": "Проверка...",
|
||||
"zh-chs": "檢查...",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1723",
|
||||
"default.handlebars->25->1750",
|
||||
"default.handlebars->25->865"
|
||||
]
|
||||
},
|
||||
@ -5518,7 +5518,7 @@
|
||||
"nl": "Wis alle meldingen",
|
||||
"zh-chs": "全部清除",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1653"
|
||||
"default.handlebars->25->1680"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -5568,7 +5568,7 @@
|
||||
"ru": "Очистить это уведомление",
|
||||
"zh-chs": "清除此通知",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1652"
|
||||
"default.handlebars->25->1679"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6380,7 +6380,7 @@
|
||||
"ru": "Подключено Intel® AMT",
|
||||
"zh-chs": "連接的英特爾®AMT",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1671"
|
||||
"default.handlebars->25->1698"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6397,7 +6397,7 @@
|
||||
"ru": "Подключенные пользователи",
|
||||
"zh-chs": "關聯用戶",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1676"
|
||||
"default.handlebars->25->1703"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6473,7 +6473,7 @@
|
||||
"ru": "Подключений ",
|
||||
"zh-chs": "連接數",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1687"
|
||||
"default.handlebars->25->1714"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6490,7 +6490,7 @@
|
||||
"ru": "Ретранслятор подключения",
|
||||
"zh-chs": "連接繼電器",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1715"
|
||||
"default.handlebars->25->1742"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6617,7 +6617,7 @@
|
||||
"ru": "Cookie-кодировщик",
|
||||
"zh-chs": "Cookie編碼器",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1701"
|
||||
"default.handlebars->25->1728"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6922,7 +6922,7 @@
|
||||
"ru": "Основной сервер",
|
||||
"zh-chs": "核心服務器",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1700"
|
||||
"default.handlebars->25->1727"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -7872,6 +7872,7 @@
|
||||
"zh-chs": "桌面",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1217",
|
||||
"default.handlebars->25->1657",
|
||||
"default.handlebars->25->432",
|
||||
"default.handlebars->container->topbar->1->1->MainSubMenuSpan->MainSubMenu->1->0->MainDevDesktop",
|
||||
"default.handlebars->contextMenu->cxdesktop"
|
||||
@ -8092,7 +8093,8 @@
|
||||
"default.handlebars->25->1238",
|
||||
"default.handlebars->25->1512",
|
||||
"default.handlebars->25->1518",
|
||||
"default.handlebars->25->1619"
|
||||
"default.handlebars->25->1619",
|
||||
"default.handlebars->25->1666"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -8132,7 +8134,7 @@
|
||||
"default.handlebars->25->1477",
|
||||
"default.handlebars->25->1499",
|
||||
"default.handlebars->25->1563",
|
||||
"default.handlebars->25->1674",
|
||||
"default.handlebars->25->1701",
|
||||
"default.handlebars->container->column_l->p2->p2info->7"
|
||||
]
|
||||
},
|
||||
@ -8185,6 +8187,7 @@
|
||||
"zh-chs": "設備名稱",
|
||||
"xloc": [
|
||||
"default-mobile.handlebars->9->232",
|
||||
"default.handlebars->25->1665",
|
||||
"default.handlebars->25->231",
|
||||
"default.handlebars->25->669",
|
||||
"player.handlebars->3->9"
|
||||
@ -9185,7 +9188,7 @@
|
||||
"nl": "Dubbele agent",
|
||||
"zh-chs": "代理重复",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1670"
|
||||
"default.handlebars->25->1697"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -9250,6 +9253,8 @@
|
||||
"ru": "Длительность",
|
||||
"zh-chs": "持續時間",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1651",
|
||||
"default.handlebars->25->1671",
|
||||
"player.handlebars->3->2"
|
||||
]
|
||||
},
|
||||
@ -9999,7 +10004,7 @@
|
||||
"nl": "Email/SMS verkeer",
|
||||
"zh-chs": "电子邮件/短信流量",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1709"
|
||||
"default.handlebars->25->1736"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -10093,6 +10098,12 @@
|
||||
"default.handlebars->container->column_l->p2->p2info->p2AccountActions->3->accountEnableNotificationsSpan->0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Enabled",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1673"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Kódování",
|
||||
"de": "Kodierung",
|
||||
@ -10110,6 +10121,12 @@
|
||||
"default-mobile.handlebars->dialog->3->dialog7->d7amtkvm->3->3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "End Time",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1670"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "angličtina",
|
||||
"de": "Englisch",
|
||||
@ -10670,7 +10687,7 @@
|
||||
"ru": "Внешний",
|
||||
"zh-chs": "外部",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1694"
|
||||
"default.handlebars->25->1721"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -10874,6 +10891,7 @@
|
||||
"zh-chs": "檔案",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1224",
|
||||
"default.handlebars->25->1658",
|
||||
"default.handlebars->container->topbar->1->1->MainSubMenuSpan->MainSubMenu->1->0->MainDevFiles",
|
||||
"default.handlebars->contextMenu->cxfiles"
|
||||
]
|
||||
@ -11176,8 +11194,8 @@
|
||||
"ru": "Свободно",
|
||||
"zh-chs": "自由",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1655",
|
||||
"default.handlebars->25->1657"
|
||||
"default.handlebars->25->1682",
|
||||
"default.handlebars->25->1684"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -12094,7 +12112,7 @@
|
||||
"ru": "Всего кучи",
|
||||
"zh-chs": "堆總數",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1696"
|
||||
"default.handlebars->25->1723"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -12111,7 +12129,7 @@
|
||||
"ru": "Куча используется",
|
||||
"zh-chs": "堆使用",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1695"
|
||||
"default.handlebars->25->1722"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -12897,8 +12915,8 @@
|
||||
"xloc": [
|
||||
"default.handlebars->25->1326",
|
||||
"default.handlebars->25->1334",
|
||||
"default.handlebars->25->1692",
|
||||
"default.handlebars->25->1714"
|
||||
"default.handlebars->25->1719",
|
||||
"default.handlebars->25->1741"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -12935,6 +12953,18 @@
|
||||
"default.handlebars->25->150"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Intel AMT Redirection",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1660"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Intel AMT WSMAN",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1659"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Intel AMT zjištěno",
|
||||
"de": "Intel AMT erkannt",
|
||||
@ -13616,7 +13646,7 @@
|
||||
"ru": "Некорректный тип группы устройств",
|
||||
"zh-chs": "無效的設備組類型",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1669"
|
||||
"default.handlebars->25->1696"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -13633,7 +13663,7 @@
|
||||
"ru": "Некорректный JSON",
|
||||
"zh-chs": "無效的JSON",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1663"
|
||||
"default.handlebars->25->1690"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -13685,7 +13715,7 @@
|
||||
"ru": "Некорректная сигнатура PKCS",
|
||||
"zh-chs": "無效的PKCS簽名",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1661"
|
||||
"default.handlebars->25->1688"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -13702,7 +13732,7 @@
|
||||
"ru": "Некорректная сигнатура RSA",
|
||||
"zh-chs": "無效的RSA密碼",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1662"
|
||||
"default.handlebars->25->1689"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -14785,7 +14815,7 @@
|
||||
"ru": "Меньше",
|
||||
"zh-chs": "減",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1731"
|
||||
"default.handlebars->25->1758"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -15882,7 +15912,7 @@
|
||||
"ru": "Сообщения главного сервера",
|
||||
"zh-chs": "主服務器消息",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1703"
|
||||
"default.handlebars->25->1730"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -16308,7 +16338,7 @@
|
||||
"ru": "Достигнуто максимальное число сессий",
|
||||
"zh-chs": "達到的會話數上限",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1667"
|
||||
"default.handlebars->25->1694"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -16362,7 +16392,7 @@
|
||||
"ru": "Мегабайт",
|
||||
"zh-chs": "兆字節",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1693"
|
||||
"default.handlebars->25->1720"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -16379,7 +16409,7 @@
|
||||
"ru": "ОЗУ",
|
||||
"zh-chs": "記憶",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1684",
|
||||
"default.handlebars->25->1711",
|
||||
"default.handlebars->25->819",
|
||||
"default.handlebars->container->column_l->p40->3->1->p40type->3"
|
||||
]
|
||||
@ -16515,7 +16545,7 @@
|
||||
"ru": "Трафик MeshAgent",
|
||||
"zh-chs": "MeshAgent流量",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1705"
|
||||
"default.handlebars->25->1732"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -16532,7 +16562,7 @@
|
||||
"ru": "Обновление MeshAgent",
|
||||
"zh-chs": "MeshAgent更新",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1706"
|
||||
"default.handlebars->25->1733"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -16635,7 +16665,7 @@
|
||||
"ru": "Соединения сервера MeshCentral",
|
||||
"zh-chs": "MeshCentral服務器對等",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1704"
|
||||
"default.handlebars->25->1731"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -16898,7 +16928,7 @@
|
||||
"ru": "Диспетчер сообщения",
|
||||
"zh-chs": "郵件調度程序",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1702"
|
||||
"default.handlebars->25->1729"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -17046,7 +17076,7 @@
|
||||
"ru": "Еще",
|
||||
"zh-chs": "更多",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1730"
|
||||
"default.handlebars->25->1757"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -17117,6 +17147,12 @@
|
||||
"default.handlebars->contextMenu->cxmdesktop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Multiplexor",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1672"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Můj účet",
|
||||
"de": "Mein Konto",
|
||||
@ -18638,6 +18674,12 @@
|
||||
"default.handlebars->25->802"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Not on server",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1664"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Nenastaveno",
|
||||
"de": "Nicht gesetzt",
|
||||
@ -18907,7 +18949,7 @@
|
||||
"ru": "Произошло в {0}",
|
||||
"zh-chs": "發生在{0}",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1651"
|
||||
"default.handlebars->25->1678"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -20011,7 +20053,7 @@
|
||||
"zh-chs": "插件動作",
|
||||
"xloc": [
|
||||
"default.handlebars->25->169",
|
||||
"default.handlebars->25->1727"
|
||||
"default.handlebars->25->1754"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -20317,6 +20359,12 @@
|
||||
"default.handlebars->25->7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Present on server",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1663"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Pro pokračování/pozastavení stiskněte [mezerník].",
|
||||
"de": "Für Abspielen/Pause [Leertaste] drücken,",
|
||||
@ -20421,6 +20469,7 @@
|
||||
"ru": "Протокол",
|
||||
"zh-chs": "協議",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1661",
|
||||
"player.handlebars->3->15"
|
||||
]
|
||||
},
|
||||
@ -20727,7 +20776,7 @@
|
||||
"ru": "RSS",
|
||||
"zh-chs": "的RSS",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1697"
|
||||
"default.handlebars->25->1724"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -20818,6 +20867,12 @@
|
||||
"default.handlebars->container->column_l->p11->deskarea0->deskarea4->1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Recording Details",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1675"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Recordings",
|
||||
"nl": "Opnames",
|
||||
@ -20885,7 +20940,8 @@
|
||||
"default.handlebars->25->437",
|
||||
"default.handlebars->container->column_l->p11->deskarea0->deskarea3x->DeskTools->deskToolsAreaTop->DeskToolsRefreshButton",
|
||||
"default.handlebars->container->column_l->p13->p13toolbar->1->2->1->3",
|
||||
"default.handlebars->container->column_l->p40->3->3"
|
||||
"default.handlebars->container->column_l->p40->3->3",
|
||||
"default.handlebars->container->column_l->p52->3->1->0->3->3"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -20938,7 +20994,7 @@
|
||||
"ru": "Число ретрансляций",
|
||||
"zh-chs": "中繼計數",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1679"
|
||||
"default.handlebars->25->1706"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -20955,7 +21011,7 @@
|
||||
"ru": "Ошибки ретранслятора",
|
||||
"zh-chs": "中繼錯誤",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1672"
|
||||
"default.handlebars->25->1699"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -20972,8 +21028,8 @@
|
||||
"ru": "Сессии ретранслятора",
|
||||
"zh-chs": "接力會議",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1678",
|
||||
"default.handlebars->25->1691"
|
||||
"default.handlebars->25->1705",
|
||||
"default.handlebars->25->1718"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23002,7 +23058,7 @@
|
||||
"ru": "Сертификат сервера",
|
||||
"zh-chs": "服務器證書",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1707"
|
||||
"default.handlebars->25->1734"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23019,7 +23075,7 @@
|
||||
"ru": "База данных сервера",
|
||||
"zh-chs": "服務器數據庫",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1708"
|
||||
"default.handlebars->25->1735"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23128,7 +23184,7 @@
|
||||
"ru": "Состояние сервера",
|
||||
"zh-chs": "服務器狀態",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1658"
|
||||
"default.handlebars->25->1685"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23162,7 +23218,7 @@
|
||||
"ru": "Трассировка сервера",
|
||||
"zh-chs": "服務器跟踪",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1718"
|
||||
"default.handlebars->25->1745"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23301,7 +23357,7 @@
|
||||
"ru": "ServerStats.csv",
|
||||
"zh-chs": "ServerStats.csv",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1699"
|
||||
"default.handlebars->25->1726"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23338,6 +23394,12 @@
|
||||
"default.handlebars->container->column_l->p11->deskarea0->deskarea3x->DeskTools->deskToolsAreaTop->deskToolsTopTabService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Session",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1649"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Čas spojení",
|
||||
"de": "Sitzungszeit",
|
||||
@ -23795,6 +23857,8 @@
|
||||
"ru": "Размер",
|
||||
"zh-chs": "尺寸",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1652",
|
||||
"default.handlebars->25->1667",
|
||||
"default.handlebars->container->column_l->p1->devListToolbarSpan->1->0->9->devListToolbarSize"
|
||||
]
|
||||
},
|
||||
@ -24602,6 +24666,13 @@
|
||||
"default.handlebars->25->705"
|
||||
]
|
||||
},
|
||||
{
|
||||
"en": "Start Time",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1650",
|
||||
"default.handlebars->25->1669"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cs": "Stav",
|
||||
"de": "Zustand",
|
||||
@ -24652,6 +24723,7 @@
|
||||
"zh-chs": "狀態",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1601",
|
||||
"default.handlebars->25->1662",
|
||||
"default.handlebars->container->column_l->p42->p42tbl->1->0->7"
|
||||
]
|
||||
},
|
||||
@ -25193,6 +25265,7 @@
|
||||
"zh-chs": "終奌站",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1221",
|
||||
"default.handlebars->25->1656",
|
||||
"default.handlebars->25->433",
|
||||
"default.handlebars->container->topbar->1->1->MainSubMenuSpan->MainSubMenu->1->0->MainDevTerminal",
|
||||
"default.handlebars->contextMenu->cxterminal"
|
||||
@ -25393,7 +25466,7 @@
|
||||
"ru": "На данный момент уведомлений нет",
|
||||
"zh-chs": "目前沒有任何通知",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1650"
|
||||
"default.handlebars->25->1677"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -26623,6 +26696,8 @@
|
||||
"default-mobile.handlebars->9->182",
|
||||
"default.handlebars->25->13",
|
||||
"default.handlebars->25->1641",
|
||||
"default.handlebars->25->1654",
|
||||
"default.handlebars->25->1655",
|
||||
"default.handlebars->25->379",
|
||||
"default.handlebars->25->41",
|
||||
"default.handlebars->25->42",
|
||||
@ -26667,7 +26742,7 @@
|
||||
"ru": "Неизвестное действие",
|
||||
"zh-chs": "未知動作",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1664"
|
||||
"default.handlebars->25->1691"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -26704,7 +26779,7 @@
|
||||
"xloc": [
|
||||
"default.handlebars->25->1510",
|
||||
"default.handlebars->25->1617",
|
||||
"default.handlebars->25->1668"
|
||||
"default.handlebars->25->1695"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -26721,7 +26796,7 @@
|
||||
"ru": "Неизвестная группа",
|
||||
"zh-chs": "未知群組",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1660"
|
||||
"default.handlebars->25->1687"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -26845,7 +26920,7 @@
|
||||
"ru": "Актуально",
|
||||
"zh-chs": "最新",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1725"
|
||||
"default.handlebars->25->1752"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -27094,8 +27169,8 @@
|
||||
"ru": "Использовано",
|
||||
"zh-chs": "用過的",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1654",
|
||||
"default.handlebars->25->1656"
|
||||
"default.handlebars->25->1681",
|
||||
"default.handlebars->25->1683"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -27116,6 +27191,7 @@
|
||||
"default.handlebars->25->1179",
|
||||
"default.handlebars->25->1402",
|
||||
"default.handlebars->25->1506",
|
||||
"default.handlebars->25->1674",
|
||||
"default.handlebars->25->193",
|
||||
"default.handlebars->25->562"
|
||||
]
|
||||
@ -27171,7 +27247,7 @@
|
||||
"ru": "Учетные записи пользователей",
|
||||
"zh-chs": "用戶帳號",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1673"
|
||||
"default.handlebars->25->1700"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -27383,7 +27459,7 @@
|
||||
"ru": "Сессии пользователя",
|
||||
"zh-chs": "用戶會話",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1690"
|
||||
"default.handlebars->25->1717"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -27572,7 +27648,7 @@
|
||||
"xloc": [
|
||||
"default.handlebars->25->1476",
|
||||
"default.handlebars->25->1498",
|
||||
"default.handlebars->25->1689",
|
||||
"default.handlebars->25->1716",
|
||||
"default.handlebars->container->topbar->1->1->UsersSubMenuSpan->UsersSubMenu->1->0->UsersGeneral"
|
||||
]
|
||||
},
|
||||
@ -27590,7 +27666,7 @@
|
||||
"ru": "Сессии пользователей",
|
||||
"zh-chs": "用戶會話",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1677"
|
||||
"default.handlebars->25->1704"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -27773,7 +27849,7 @@
|
||||
"ru": "Версия несовместима, пожалуйста, сначала обновите установку MeshCentral",
|
||||
"zh-chs": "版本不兼容,请先升级您的MeshCentral安装",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1721"
|
||||
"default.handlebars->25->1748"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -27841,8 +27917,8 @@
|
||||
"ru": "Просмотр журнала изменений",
|
||||
"zh-chs": "查看变更日志",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1724",
|
||||
"default.handlebars->25->1726"
|
||||
"default.handlebars->25->1751",
|
||||
"default.handlebars->25->1753"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -28100,8 +28176,8 @@
|
||||
"ru": "Веб-сервер",
|
||||
"zh-chs": "網絡服務器",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1710",
|
||||
"default.handlebars->25->1711"
|
||||
"default.handlebars->25->1737",
|
||||
"default.handlebars->25->1738"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -28118,7 +28194,7 @@
|
||||
"ru": "Запросы веб-сервера",
|
||||
"zh-chs": "Web服務器請求",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1712"
|
||||
"default.handlebars->25->1739"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -28135,7 +28211,7 @@
|
||||
"ru": "Ретранслятор Web Socket",
|
||||
"zh-chs": "Web套接字中繼",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1713"
|
||||
"default.handlebars->25->1740"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -29200,7 +29276,7 @@
|
||||
"ru": "\\\\'",
|
||||
"zh-chs": "\\\\'",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1722"
|
||||
"default.handlebars->25->1749"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -29475,7 +29551,7 @@
|
||||
"ru": "свободно",
|
||||
"zh-chs": "自由",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1685"
|
||||
"default.handlebars->25->1712"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -29839,7 +29915,7 @@
|
||||
"ru": "servertrace.csv",
|
||||
"zh-chs": "servertrace.csv",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1720"
|
||||
"default.handlebars->25->1747"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -29892,7 +29968,7 @@
|
||||
"ru": "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss",
|
||||
"zh-chs": "時間,conn.agent,conn.users,conn.usersessions,conn.relaysession,conn.intelamt,mem.external,mem.heapused,mem.heaptotal,mem.rss",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1698"
|
||||
"default.handlebars->25->1725"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -29909,7 +29985,7 @@
|
||||
"ru": "time, source, message",
|
||||
"zh-chs": "時間,來源,訊息",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1719"
|
||||
"default.handlebars->25->1746"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -29940,7 +30016,7 @@
|
||||
"ru": "всего",
|
||||
"zh-chs": "總",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1686"
|
||||
"default.handlebars->25->1713"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -30094,7 +30170,8 @@
|
||||
"ru": "{0} Kб",
|
||||
"zh-chs": "{0} Kb",
|
||||
"xloc": [
|
||||
"default.handlebars->25->1349"
|
||||
"default.handlebars->25->1349",
|
||||
"default.handlebars->25->1653"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -30181,7 +30258,8 @@
|
||||
"zh-chs": "{0}個字節",
|
||||
"xloc": [
|
||||
"default-mobile.handlebars->9->88",
|
||||
"default.handlebars->25->1359"
|
||||
"default.handlebars->25->1359",
|
||||
"default.handlebars->25->1668"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -30796,4 +30874,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1047,12 +1047,13 @@
|
||||
<input type=button onclick=openRecodringPlayer() value="Open Player..." />
|
||||
</div>
|
||||
<div>
|
||||
<input type=button onclick=refreshRecodings() value="Refresh" />
|
||||
</div>
|
||||
</td>
|
||||
<td class="h2"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id=p52recordings style=""></div>
|
||||
<div id=p52recordings style="overflow-y:auto"></div>
|
||||
</div>
|
||||
<br id="column_l_bottomgap" />
|
||||
</div>
|
||||
@ -1489,6 +1490,8 @@
|
||||
QS('p31events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
|
||||
QS('p41events')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
|
||||
QS('p41events')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
|
||||
QS('p52recordings')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
|
||||
QS('p52recordings')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
|
||||
|
||||
// We are looking at a single device, remove all the back buttons
|
||||
if ('{{currentNode}}'.toLowerCase() != '') {
|
||||
@ -2094,6 +2097,11 @@
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'recordings': {
|
||||
p52recordings = message.events;
|
||||
updateRecordings();
|
||||
break;
|
||||
}
|
||||
case 'getcookie': {
|
||||
if (message.tag == 'clickonce') {
|
||||
if (message.trustedCert == true) {
|
||||
@ -2278,6 +2286,10 @@
|
||||
}
|
||||
if (message.event.noact) break; // Take no action on this event
|
||||
switch (message.event.action) {
|
||||
case 'recording': {
|
||||
if (p52recordings != null) { p52recordings.unshift(message.event); message.event.present = 1; updateRecordings(); }
|
||||
break;
|
||||
}
|
||||
case 'userWebState': {
|
||||
// New user web state, update the web page as needed
|
||||
try {
|
||||
@ -6595,7 +6607,7 @@
|
||||
function deskSaveImage() {
|
||||
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
||||
var d = new Date(), n = 'Desktop-' + currentNode.name + '-' + d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + '-' + ('0' + d.getHours()).slice(-2) + '-' + ('0' + d.getMinutes()).slice(-2);
|
||||
Q('Desk')['toBlob'](function (blob) { saveAs(blob, n + '.jpg'); });
|
||||
Q('Desk')['toBlob'](function (blob) { saveAs(blob, n + '.png'); });
|
||||
}
|
||||
|
||||
function deskDisplayInfo(sender, displays, selDisplay) {
|
||||
@ -11124,15 +11136,92 @@
|
||||
|
||||
var p52recordings = null;
|
||||
function updateRecordings() {
|
||||
var x = 'Under construction';
|
||||
// Display the users using the sorted list
|
||||
var x = '<table class=p3usersTable cellpadding=0 cellspacing=0>', addHeader = true;
|
||||
x += '<th>' + "Session" + '<th style=width:110px>' + nobreak("Start Time") + '<th style=width:110px>' + "Duration" + '<th style=width:110px>' + "Size";
|
||||
if (p52recordings != null) {
|
||||
var recdate = null;
|
||||
for (var i in p52recordings) {
|
||||
var rec = p52recordings[i], rect = new Date(rec.time), day = printDate(rect);
|
||||
if (day != recdate) { recdate = day; x += '<tr><td class=userTableHeader colspan=4>' + day; }
|
||||
x += addRecordingHtml(i, rec);
|
||||
}
|
||||
}
|
||||
x += '</table>';
|
||||
QH('p52recordings', x);
|
||||
}
|
||||
|
||||
function openRecodringPlayer() {
|
||||
if (xxdialogMode) return;
|
||||
window.open(window.location.origin + '{{{domainurl}}}player.htm', 'meshcentral-deskplayer');
|
||||
function addRecordingHtml(i, rec) {
|
||||
var sessionLengthStr = '';
|
||||
if (rec.lengthTime) { sessionLengthStr = pad2(Math.floor(rec.lengthTime / 3600)) + ':' + pad2(Math.floor((rec.lengthTime % 3600) / 60)) + ':' + pad2(Math.floor(rec.lengthTime % 60)); }
|
||||
var sessionStartStr = printTime(new Date(rec.time));
|
||||
if (rec.sessionStart) { sessionStartStr = printTime(new Date(rec.sessionStart)); }
|
||||
var sessionSize = '';
|
||||
if (rec.size) { sessionSize = format("{0} Kb", Math.round(rec.size / 1024)); }
|
||||
var sessionName = '<i>' + "Unknown" + '</i>';
|
||||
if (rec.name && rec.meshname) {
|
||||
var recmesh = meshes[rec.meshid];
|
||||
if (recmesh != null) {
|
||||
sessionName = '<a href=# onclick=\'gotoMesh("' + rec.meshid + '")\'>' + EscapeHtml(rec.meshname) + '</a> - <a href=# onclick=\'gotoDevice("' + rec.nodeid + '",10)\'>' + EscapeHtml(rec.name) + '</a>';
|
||||
} else {
|
||||
sessionName = EscapeHtml(rec.meshname) + ' - ' + EscapeHtml(rec.name);
|
||||
}
|
||||
}
|
||||
if ((rec.userids != null) && (rec.userids.length > 0)) {
|
||||
if (rec.userids.length > 1) {
|
||||
sessionName += ' - ' + format('{0} users', rec.userids.length);
|
||||
} else {
|
||||
var ruser = null;
|
||||
if (users != null) { ruser = users[rec.userids[0]]; }
|
||||
if (ruser != null) {
|
||||
sessionName += ' - <a href=# onclick=\'gotoUser("' + rec.userids[0] + '")\'>' + EscapeHtml(ruser.name) + '</a>';
|
||||
} else {
|
||||
sessionName += ' - ' + EscapeHtml(rec.userids[0].split('/')[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
var actions = '', icon = 'm0';
|
||||
if (rec.present == 1) { icon = 'm1'; actions = '<div style=cursor:pointer;float:right><a href=recordings.ashx?file=' + encodeURIComponent(rec.filename) + ' download><img src=images/link4.png height=10 width=10 title="Download Recording" download></a> </div>'; }
|
||||
var x = '<tr tabindex=0 onmouseover=userMouseHover2(this,1) onmouseout=userMouseHover2(this,0) onkeypress="if (event.key==\'Enter\') showRecordingDialog(event,\'' + i + '\')"><td style=cursor:pointer>';
|
||||
x += '<div class=bar style=width:100%>';
|
||||
x += '<div class=baricon><input class=RecordingCheckbox value=' + i + ' onclick=p52updateInfo() type=checkbox></div>';
|
||||
x += '<div class=baricon onclick=showRecordingDialog(event,"' + i + '")><div class=' + icon + '></div></div>';
|
||||
x += '<div class=g1 onclick=showRecordingDialog(event,"' + i + '")></div><div class=g2 onclick=showRecordingDialog("' + i + '")></div>';
|
||||
x += '<div onclick=showRecordingDialog(event,"' + i + '")>' + actions + '<div style=font-size:16px>' + sessionName + '</div></div></div><td style=text-align:center>' + sessionStartStr + '<td style=text-align:center>' + sessionLengthStr + '<td style=text-align:center>' + sessionSize;
|
||||
return x;
|
||||
}
|
||||
|
||||
function showRecordingDialog(event, i) {
|
||||
if (xxdialogMode) return;
|
||||
if ((event.target.tagName == 'IMG') || (event.target.tagName == 'A')) return;
|
||||
var rec = p52recordings[i];
|
||||
//console.log(rec);
|
||||
var x = '';
|
||||
if (rec.protocol) {
|
||||
var protocolStr = "Unknown";
|
||||
if (rec.protocol == 1) { protocolStr = "Terminal"; }
|
||||
if (rec.protocol == 2) { protocolStr = "Desktop"; }
|
||||
if (rec.protocol == 5) { protocolStr = "Files"; }
|
||||
if (rec.protocol == 100) { protocolStr = "Intel AMT WSMAN"; }
|
||||
if (rec.protocol == 101) { protocolStr = "Intel AMT Redirection"; }
|
||||
x += addHtmlValue4("Protocol", protocolStr);
|
||||
}
|
||||
x += addHtmlValue4("Status", (rec.present == 1)?"Present on server":"Not on server");
|
||||
if (rec.name) { x += addHtmlValue4("Device Name", EscapeHtml(rec.name)); }
|
||||
if (rec.meshname) { x += addHtmlValue4("Device Group", EscapeHtml(rec.meshname)); }
|
||||
if (rec.size) { x += addHtmlValue4("Size", format("{0} bytes", rec.size)); }
|
||||
if (rec.startTime) { x += addHtmlValue4("Start Time", printTime(new Date(rec.startTime))); }
|
||||
if (rec.time) { x += addHtmlValue4("End Time", printTime(new Date(rec.time))); }
|
||||
if (rec.lengthTime) { x += addHtmlValue4("Duration", pad2(Math.floor(rec.lengthTime / 3600)) + ':' + pad2(Math.floor((rec.lengthTime % 3600) / 60)) + ':' + pad2(Math.floor(rec.lengthTime % 60))); }
|
||||
if (rec.multiplex == true) { x += addHtmlValue4("Multiplexor", "Enabled"); }
|
||||
if (rec.userids) { for (var i in rec.userids) { x += addHtmlValue4("User", rec.userids[i].split('/')[2]); } }
|
||||
setDialogMode(2, "Recording Details", 9, null, x);
|
||||
}
|
||||
|
||||
function refreshRecodings() { meshserver.send({ action: 'recordings', limit: 1000 }); }
|
||||
function openRecodringPlayer() { if (!xxdialogMode) window.open(window.location.origin + '{{{domainurl}}}player.htm', 'meshcentral-deskplayer'); }
|
||||
function p52updateInfo() { }
|
||||
|
||||
//
|
||||
// FILE SELECTOR, DIALOG 3
|
||||
//
|
||||
@ -11856,7 +11945,7 @@
|
||||
if (x == 21) { p21updateMesh(); }
|
||||
|
||||
// Update Recordings
|
||||
if (x == 52) { updateRecordings(); }
|
||||
if (x == 52) { if (p52recordings == null) { refreshRecodings(); } updateRecordings(); }
|
||||
|
||||
// Update the web page title
|
||||
if ((currentNode) && (x >= 10) && (x < 20)) {
|
||||
@ -12223,6 +12312,7 @@
|
||||
function addHtmlValue(t, v) { return '<table><td style=width:120px>' + t + '<td><b>' + v + '</b></table>'; }
|
||||
function addHtmlValue2(t, v) { return '<div><div style=display:inline-block;float:right>' + v + '</div><div style=display:inline-block>' + t + '</div></div>'; }
|
||||
function addHtmlValue3(t, v) { return '<div><b>' + t + '</b></div><div style=margin-left:16px>' + v + '</div>'; }
|
||||
function addHtmlValue4(t, v) { return '<table style=width:100%><td style=width:120px>' + t + '<td style=text-align:right><b>' + v + '</b></table>'; }
|
||||
function parseUriArgs() { var href = window.document.location.href; if (href.endsWith('#')) { href = href.substring(0, href.length - 1); } var name, r = {}, parsedUri = href.split(/[\?&|\=]/); parsedUri.splice(0, 1); for (x in parsedUri) { switch (x % 2) { case 0: { name = decodeURIComponent(parsedUri[x]); break; } case 1: { r[name] = decodeURIComponent(parsedUri[x]); var x = parseInt(r[name]); if (x == r[name]) { r[name] = x; } break; } default: { break; } } } return r; }
|
||||
function focusTextBox(x) { setTimeout(function(){ Q(x).selectionStart = Q(x).selectionEnd = 65535; Q(x).focus(); }, 0); }
|
||||
function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } // New version
|
||||
@ -12241,6 +12331,7 @@
|
||||
function getOrderedList(objList, oname) { var r = []; for (var i in objList) { r.push(objList[i]); } r.sort(function(a, b) { var aa = a[oname].toLowerCase(), bb = b[oname].toLowerCase(); if (aa > bb) return 1; if (aa < bb) return -1; return 0; }); return r; }
|
||||
function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); }
|
||||
function nobreak(x) { return x.split(' ').join(' '); }
|
||||
function pad2(num) { var s = '00' + num; return s.substr(s.length - 2); }
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
@ -94,6 +94,7 @@
|
||||
var recFileMetadata = null;
|
||||
var recFileProtocol = 0;
|
||||
var recFileIndexBasePtr = null;
|
||||
var recFileExtras = null;
|
||||
var agentDesktop = null;
|
||||
var amtDesktop = null;
|
||||
var playing = false;
|
||||
@ -427,7 +428,6 @@
|
||||
cleanup();
|
||||
recFile = files[0];
|
||||
recFilePtr = 0;
|
||||
readNextBlock(processFirstBlock);
|
||||
readLastBlock(function (type, flags, time, extras) {
|
||||
if (type == 3) {
|
||||
// File is ok
|
||||
|
27
webserver.js
27
webserver.js
@ -2437,6 +2437,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
}
|
||||
}
|
||||
|
||||
// Download a desktop recording
|
||||
function handleGetRecordings(req, res) {
|
||||
const domain = checkUserIpAddress(req, res);
|
||||
if (domain == null) { return; }
|
||||
|
||||
// Check the query
|
||||
if (req.query.file == null) { res.sendStatus(401); return; }
|
||||
|
||||
// Get the recording path
|
||||
var recordingsPath = null;
|
||||
if (domain.sessionrecording.filepath) { recordingsPath = domain.sessionrecording.filepath; } else { recordingsPath = parent.recordpath; }
|
||||
if (recordingsPath == null) { res.sendStatus(401); return; }
|
||||
|
||||
// Get the user and check user rights
|
||||
var authUserid = null;
|
||||
if ((req.session != null) && (typeof req.session.userid == 'string')) { authUserid = req.session.userid; }
|
||||
if (authUserid == null) { res.sendStatus(401); return; }
|
||||
const user = obj.users[authUserid];
|
||||
if (user == null) { res.sendStatus(401); return; }
|
||||
if ((user.siteadmin & 512) == 0) { res.sendStatus(401); return; } // Check if we have right to get recordings
|
||||
|
||||
// Send the recorded file
|
||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=\"' + req.query.file + '\"' });
|
||||
try { res.sendFile(obj.path.join(recordingsPath, req.query.file)); } catch (ex) { res.sendStatus(404); }
|
||||
}
|
||||
|
||||
// Server the player page
|
||||
function handlePlayerRequest(req, res) {
|
||||
const domain = checkUserIpAddress(req, res);
|
||||
@ -3917,6 +3943,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
obj.app.get(url + 'logo.png', handleLogoRequest);
|
||||
obj.app.post(url + 'translations', handleTranslationsRequest);
|
||||
obj.app.get(url + 'welcome.jpg', handleWelcomeImageRequest);
|
||||
obj.app.get(url + 'recordings.ashx', handleGetRecordings);
|
||||
obj.app.get(url + 'player.htm', handlePlayerRequest);
|
||||
obj.app.get(url + 'player', handlePlayerRequest);
|
||||
obj.app.ws(url + 'amtactivate', handleAmtActivateWebSocket);
|
||||
|
Loading…
Reference in New Issue
Block a user