Reporting improvements.
Ylian Saint-Hilaire committed
Sep 10, 2021 at 17:28 UTC
ea027d73a16744bcac8174f169a5bb9e481434d8
3 files changed
+143
-38
apprelays.js
+99
-32
@@ -22,6 +22,18 @@ Protocol numbers
22
13 - SSH-FILES
23
*/
24
25
+// Protocol Numbers
26
+const PROTOCOL_TERMINAL = 1;
27
+const PROTOCOL_DESKTOP = 2;
28
+const PROTOCOL_FILES = 5;
29
+const PROTOCOL_AMTWSMAN = 100;
30
+const PROTOCOL_AMTREDIR = 101;
31
+const PROTOCOL_MESSENGER = 200;
32
+const PROTOCOL_WEBRDP = 201;
33
+const PROTOCOL_WEBSSH = 202;
34
+const PROTOCOL_WEBSFTP = 203;
35
+const PROTOCOL_WEBVNC = 204;
36
+
37
38
// Construct a MSTSC Relay object, called upon connection
39
// This is a bit of a hack as we are going to run the RDP connection thru a loopback connection.
@@ -42,11 +54,19 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
54
obj.close = function (arg) {
55
if (obj.ws == null) return;
56
45
- // Collect how many raw bytes where received and sent.
46
- // We sum both the websocket and TCP client in this case.
47
- //var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
48
- //if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
49
- //console.log('WinRDP - in', inTraffc, 'out', outTraffc);
57
+ // Event the session ending
58
+ if ((obj.startTime) && (obj.meshid != null)) {
59
+ // Collect how many raw bytes where received and sent.
60
+ // We sum both the websocket and TCP client in this case.
61
+ var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
62
+ if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
63
+ const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
64
+ var user = parent.users[obj.cookie.userid];
65
+ var username = (user != null) ? user.name : null;
66
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.cookie.userid, username: username, msgid: 125, msgArgs: [sessionSeconds], msg: "Left Web-RDP session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBRDP, bytesin: inTraffc, bytesout: outTraffc };
67
+ parent.parent.DispatchEvent(['*', obj.nodeid, obj.cookie.userid, obj.meshid], obj, event);
68
+ delete obj.startTime;
69
+ }
70
71
if (obj.wsClient) { obj.wsClient.close(); delete obj.wsClient; }
72
if (obj.tcpServer) { obj.tcpServer.close(); delete obj.tcpServer; }
@@ -55,7 +75,11 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
75
if (arg == 2) { try { ws._socket._parent.end(); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
76
obj.ws.removeAllListeners();
77
obj.relayActive = false;
78
+
79
delete obj.ws;
80
+ delete obj.nodeid;
81
+ delete obj.meshid;
82
+ delete obj.userid;
83
};
84
85
// Start the looppback server
@@ -116,6 +140,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
140
}).on('connect', function () {
141
send(['rdp-connect']);
142
if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed
143
+ obj.startTime = Date.now();
144
}).on('bitmap', function (bitmap) {
145
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
146
delete bitmap.data;
@@ -148,7 +173,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
173
// Event node change if needed
174
if (changed) {
175
// Event the node change
151
- var event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" };
176
+ var event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.cookie.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" };
177
if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
178
parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event);
179
}
@@ -166,16 +191,18 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
191
192
// Decode the authentication cookie
193
obj.cookie = parent.parent.decodeCookie(obj.infos.ip, parent.parent.loginCookieEncryptionKey);
169
- if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) return;
194
+ if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) { obj.close(); return; }
195
obj.nodeid = obj.cookie.nodeid;
196
obj.userid = obj.cookie.userid;
197
173
- // Check is you need to load server stored credentials
174
- if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) {
175
- parent.parent.db.Get(obj.nodeid, function (err, nodes) {
176
- if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
177
- const node = nodes[0];
198
+ // Get node
199
+ parent.parent.db.Get(obj.nodeid, function (err, nodes) {
200
+ if ((err != null) || (nodes == null) || (nodes.length != 1)) { obj.close(); return; }
201
+ const node = nodes[0];
202
+ obj.meshid = node.meshid;
203
204
+ // Check if we need to load server stored credentials
205
+ if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) {
206
// Check if RDP credentials exist
207
if ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) {
208
obj.infos.domain = node.rdp.d;
@@ -189,10 +216,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
216
obj.infos.password = '';
217
startTcpServer();
218
}
192
- });
193
- } else {
194
- startTcpServer();
195
- }
219
+ } else {
220
+ startTcpServer();
221
+ }
222
+ });
223
break;
224
}
225
case 'mouse': { if (rdpClient) { rdpClient.sendPointerEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
@@ -253,11 +280,19 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
280
obj.close = function (arg) {
281
if (obj.ws == null) return;
282
256
- // Collect how many raw bytes where received and sent.
257
- // We sum both the websocket and TCP client in this case.
258
- //var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
259
- //if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
260
- //console.log('WinSSH - in', inTraffc, 'out', outTraffc);
283
+ // Event the session ending
284
+ if ((obj.startTime) && (obj.meshid != null)) {
285
+ // Collect how many raw bytes where received and sent.
286
+ // We sum both the websocket and TCP client in this case.
287
+ var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
288
+ if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
289
+ const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
290
+ var user = parent.users[obj.cookie.userid];
291
+ var username = (user != null) ? user.name : null;
292
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.cookie.userid, username: username, msgid: 123, msgArgs: [sessionSeconds], msg: "Left Web-SSH session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBSSH, bytesin: inTraffc, bytesout: outTraffc };
293
+ parent.parent.DispatchEvent(['*', obj.nodeid, obj.cookie.userid, obj.meshid], obj, event);
294
+ delete obj.startTime;
295
+ }
296
297
if (obj.sshShell) {
298
obj.sshShell.destroy();
@@ -287,6 +322,8 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
322
obj.relayActive = false;
323
delete obj.termSize;
324
delete obj.cookie;
325
+ delete obj.nodeid;
326
+ delete obj.meshid;
327
delete obj.ws;
328
};
329
@@ -347,6 +384,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
384
obj.sshClient.on('ready', function () { // Authentication was successful.
385
// If requested, save the credentials
386
if (obj.keep === true) saveSshCredentials();
387
+ obj.startTime = Date.now();
388
389
obj.sshClient.shell(function (err, stream) { // Start a remote shell
390
if (err) { obj.close(); return; }
@@ -466,7 +504,15 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
504
505
// If the web socket is closed
506
ws.on('close', function (req) { parent.parent.debug('relay', 'SSH: Browser websocket closed'); obj.close(); });
469
-
507
+
508
+ // Get the meshid for this device
509
+ parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
510
+ if ((err != null) || (nodes == null) || (nodes.length != 1)) { parent.parent.debug('relay', 'SSH: Invalid device'); obj.close(); }
511
+ const node = nodes[0];
512
+ obj.nodeid = node._id; // Store the NodeID
513
+ obj.meshid = node.meshid; // Store the MeshID
514
+ });
515
+
516
return obj;
517
};
518
@@ -497,11 +543,17 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
543
obj.close = function (arg) {
544
if (obj.ws == null) return;
545
500
- // Collect how many raw bytes where received and sent.
501
- // We sum both the websocket and TCP client in this case.
502
- //var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
503
- //if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
504
- //console.log('WinSSH - in', inTraffc, 'out', outTraffc);
546
+ // Event the session ending
547
+ if (obj.startTime) {
548
+ // Collect how many raw bytes where received and sent.
549
+ // We sum both the websocket and TCP client in this case.
550
+ var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
551
+ if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
552
+ const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
553
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: user._id, username: user.name, msgid: 123, msgArgs: [sessionSeconds], msg: "Left Web-SSH session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBSSH, bytesin: inTraffc, bytesout: outTraffc };
554
+ parent.parent.DispatchEvent(['*', obj.nodeid, user._id, obj.meshid], obj, event);
555
+ delete obj.startTime;
556
+ }
557
558
if (obj.sshShell) {
559
obj.sshShell.destroy();
@@ -531,6 +583,8 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
583
obj.relayActive = false;
584
delete obj.termSize;
585
delete obj.cookie;
586
+ delete obj.nodeid;
587
+ delete obj.meshid;
588
delete obj.ws;
589
};
590
@@ -587,6 +641,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
641
obj.sshClient.on('ready', function () { // Authentication was successful.
642
// If requested, save the credentials
643
if (obj.keep === true) saveSshCredentials();
644
+ obj.startTime = Date.now();
645
646
obj.sshClient.shell(function (err, stream) { // Start a remote shell
647
if (err) { obj.close(); return; }
@@ -712,6 +767,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
767
if ((rights != 0xFFFFFFFF) && (rights & 0x00000200)) { obj.close(); return; } // MESHRIGHT_NOTERMINAL is set
768
obj.mtype = node.mtype; // Store the device group type
769
obj.nodeid = node._id; // Store the NodeID
770
+ obj.meshid = node.meshid; // Store the MeshID
771
772
// Check the SSH port
773
obj.tcpport = 22;
@@ -776,11 +832,17 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
832
obj.close = function (arg) {
833
if (obj.ws == null) return;
834
779
- // Collect how many raw bytes where received and sent.
780
- // We sum both the websocket and TCP client in this case.
781
- //var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
782
- //if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
783
- //console.log('WinSSH - in', inTraffc, 'out', outTraffc);
835
+ // Event the session ending
836
+ if (obj.startTime) {
837
+ // Collect how many raw bytes where received and sent.
838
+ // We sum both the websocket and TCP client in this case.
839
+ var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
840
+ if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
841
+ const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
842
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: user._id, username: user.name, msgid: 124, msgArgs: [sessionSeconds], msg: "Left Web-SFTP session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBSFTP, bytesin: inTraffc, bytesout: outTraffc };
843
+ parent.parent.DispatchEvent(['*', obj.nodeid, user._id, obj.meshid], obj, event);
844
+ delete obj.startTime;
845
+ }
846
847
if (obj.sshClient) {
848
obj.sshClient.destroy();
@@ -803,6 +865,8 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
865
obj.relayActive = false;
866
delete obj.cookie;
867
delete obj.sftp;
868
+ delete obj.nodeid;
869
+ delete obj.meshid;
870
delete obj.ws;
871
};
872
@@ -859,6 +923,8 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
923
obj.sshClient.on('ready', function () { // Authentication was successful.
924
// If requested, save the credentials
925
if (obj.keep === true) saveSshCredentials();
926
+ obj.startTime = Date.now();
927
+
928
obj.sshClient.sftp(function(err, sftp) {
929
if (err) { obj.close(); return; }
930
obj.connected = true;
@@ -1165,6 +1231,7 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1231
if ((rights != 0xFFFFFFFF) && (rights & 0x00000200)) { obj.close(); return; } // MESHRIGHT_NOTERMINAL is set
1232
obj.mtype = node.mtype; // Store the device group type
1233
obj.nodeid = node._id; // Store the NodeID
1234
+ obj.meshid = node.meshid; // Store the MeshID
1235
1236
// Check the SSH port
1237
obj.tcpport = 22;
meshuser.js
+24
-3
@@ -69,6 +69,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
69
const SITERIGHT_ALLEVENTS = 0x00000800; // 2048
70
const SITERIGHT_ADMIN = 0xFFFFFFFF;
71
72
+ // Protocol Numbers
73
+ const PROTOCOL_TERMINAL = 1;
74
+ const PROTOCOL_DESKTOP = 2;
75
+ const PROTOCOL_FILES = 5;
76
+ const PROTOCOL_AMTWSMAN = 100;
77
+ const PROTOCOL_AMTREDIR = 101;
78
+ const PROTOCOL_MESSENGER = 200;
79
+ const PROTOCOL_WEBRDP = 201;
80
+ const PROTOCOL_WEBSSH = 202;
81
+ const PROTOCOL_WEBSFTP = 203;
82
+ const PROTOCOL_WEBVNC = 204;
83
+
84
// Events
85
/*
86
var eventsMessageId = {
@@ -5428,7 +5440,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5440
5441
// Get the events in the time range
5442
// MySQL or MariaDB query will ignore the MsgID filter.
5431
- db.GetEventsTimeRange(ids, domain.id, [5, 10, 11, 12, 122], new Date(command.start * 1000), new Date(command.end * 1000), function (err, docs) {
5443
+ db.GetEventsTimeRange(ids, domain.id, [5, 10, 11, 12, 122, 123, 124, 125, 126], new Date(command.start * 1000), new Date(command.end * 1000), function (err, docs) {
5444
if (err != null) return;
5445
var data = { groups: {} };
5446
@@ -5443,10 +5455,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5455
data.columns = [{ id: 'time', title: "time", format: 'time' }, { id: 'nodeid', title: "device", format: 'node' }, { id: 'userid', title: "user", format: 'user' }, { id: 'protocol', title: "session", format: 'protocol', align: 'center' }, { id: 'length', title: "length", format: 'seconds', align: 'center', sumBy: 'protocol' } ];
5456
}
5457
5458
+ // Add traffic colums
5459
+ if (command.showTraffic) {
5460
+ data.columns.push({ id: 'bytesin', title: "Bytes In", format: 'bytes', align: 'center', sumBy: 'protocol' });
5461
+ data.columns.push({ id: 'bytesout', title: "Bytes Out", format: 'bytes', align: 'center', sumBy: 'protocol' });
5462
+ }
5463
+
5464
// Rows
5465
for (var i in docs) {
5466
// If MySQL or MariaDB query, we can't filter on MsgID, so we have to do it here.
5449
- if ((docs[i].msgid != 5) && (docs[i].msgid != 10) && (docs[i].msgid != 11) && (docs[i].msgid != 12) && (docs[i].msgid != 122)) continue;
5467
+ if ((docs[i].msgid != 5) && (docs[i].msgid != 10) && (docs[i].msgid != 11) && (docs[i].msgid != 12) && ((docs[i].msgid < 122) && (docs[i].msgid > 126))) continue;
5468
if ((command.devGroup != null) && (docs[i].ids != null) && (docs[i].ids.indexOf(command.devGroup) == -1)) continue;
5469
5470
var entry = { time: docs[i].time.valueOf() };
@@ -5456,9 +5474,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5474
if (command.groupBy != 2) { entry.nodeid = docs[i].nodeid; }
5475
entry.protocol = docs[i].protocol;
5476
5477
+ // Add traffic data
5478
+ if (command.showTraffic) { entry.bytesin = docs[i].bytesin; entry.bytesout = docs[i].bytesout; }
5479
+
5480
// Session length
5481
if (((docs[i].msgid >= 10) && (docs[i].msgid <= 12)) && (docs[i].msgArgs != null) && (typeof docs[i].msgArgs == 'object') && (typeof docs[i].msgArgs[3] == 'number')) { entry.length = docs[i].msgArgs[3]; }
5461
- else if ((docs[i].msgid == 122) && (docs[i].msgArgs != null) && (typeof docs[i].msgArgs == 'object') && (typeof docs[i].msgArgs[0] == 'number')) { entry.length = docs[i].msgArgs[0]; }
5482
+ else if ((docs[i].msgid >= 122) && (docs[i].msgid <= 126) && (docs[i].msgArgs != null) && (typeof docs[i].msgArgs == 'object') && (typeof docs[i].msgArgs[0] == 'number')) { entry.length = docs[i].msgArgs[0]; }
5483
5484
if (command.groupBy == 1) { // Add entry to per user group
5485
if (data.groups[docs[i].userid] == null) { data.groups[docs[i].userid] = { entries: [] }; }
views/default.handlebars
+20
-3
@@ -12892,6 +12892,10 @@
12892
120: "Started local relay session \"{0}\", protocol {1} to {2}",
12893
121: "Ended local relay session \"{0}\", protocol {1} to {2}, {3} second(s)",
12894
122: "Left the desktop multiplex session after {0} second(s).",
12895
+ 123: "Left Web-SSH session after {0} second(s).",
12896
+ 124: "Left Web-SFTP session after {0} second(s).",
12897
+ 125: "Left Web-RDP session after {0} second(s).",
12898
+ 126: "Left Web-VNC session after {0} second(s)."
12899
};
12900
12901
// Highlights the device being hovered
@@ -14942,6 +14946,10 @@
14946
x += addHtmlValue("Time Range", '<input id=d2timeRangeSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
14947
x += '</div>';
14948
14949
+ x += '<div id=d2showTrafficDiv style=display:none>';
14950
+ x += addHtmlValue("", '<div style=width:250px><label><input type=checkbox id=d2showTraffic ' + ((settings.showTraffic) ? 'checked' : '') + '>' + "Show Traffic" + '</label><div>');
14951
+ x += '</div>';
14952
+
14953
setDialogMode(2, "Generate Report", 3, generateReportDialogEx, x);
14954
generateReportDialogValidate();
14955
@@ -14953,6 +14961,7 @@
14961
14962
function generateReportDialogValidate() {
14963
QV('d2timeRangeDiv', Q('d2timeRange').value == 0);
14964
+ QV('d2showTrafficDiv', Q('d2reportType').value == 1);
14965
}
14966
14967
function generateReportDialogEx(b, tag) {
@@ -14969,8 +14978,8 @@
14978
try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone; } catch (ex) {}
14979
var devGroup = decodeURIComponent(Q('d2groupId').value);
14980
if (devGroup == 0) { devGroup = null; }
14972
- putstore('_ReportSettings', JSON.stringify({ type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), timeRange: parseInt(Q('d2timeRange').value), devGroup: devGroup }));
14973
- meshserver.send({ action: 'report', type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), start: start, end: end, tz: tz, tf: new Date().getTimezoneOffset(), l: getLang(), devGroup: devGroup });
14981
+ putstore('_ReportSettings', JSON.stringify({ type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), timeRange: parseInt(Q('d2timeRange').value), devGroup: devGroup, showTraffic: Q('d2showTraffic').checked }));
14982
+ meshserver.send({ action: 'report', type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), start: start, end: end, tz: tz, tf: new Date().getTimezoneOffset(), l: getLang(), devGroup: devGroup, showTraffic: Q('d2showTraffic').checked });
14983
}
14984
14985
function renderReport(r) {
@@ -15071,13 +15080,21 @@
15080
}
15081
15082
function renderReportFormat(v, f) {
15083
+ if (v == null) return '';
15084
if (f == 'datetime') { return printDateTime(new Date(v)).split(' ').join(' '); }
15085
if (f == 'time') { return printTime(new Date(v)).split(' ').join(' '); }
15086
if (f == 'protocol') {
15087
if (v == 1) return "Terminal";
15088
if (v == 2) return "Desktop";
15089
if (v == 5) return "Files";
15080
- return "Unknown";
15090
+ if (v == 100) return "AMT-WSMAN";
15091
+ if (v == 101) return "AMT-Redir";
15092
+ if (v == 200) return "Messenger";
15093
+ if (v == 201) return "Web-RDP";
15094
+ if (v == 202) return "Web-SSH";
15095
+ if (v == 203) return "Web-SFTP";
15096
+ if (v == 204) return "Web-VNC";
15097
+ return "Unknown" + ' (' + v + ')';
15098
}
15099
if (f == 'seconds') {
15100
var seconds = v % 60;