1
0
Fork 0
mirror of https://github.com/Ylianst/MeshCentral.git synced 2025-03-09 15:40:18 +00:00

Compare commits

...

71 commits

Author SHA1 Message Date
si458
88a765bb13 add agentupdate to console agent actions #6869
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-03-08 18:37:18 +00:00
Daniel Hammerschmidt
7ad4b917be
print stack trace on plugin error and add space for readability of error message (#6859)
* add space for readability of error message

* print stack trace on plugin error
2025-03-08 17:50:39 +00:00
si458
d10173a018 fix sharing-mobile view only having control #6764
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-03-08 17:20:30 +00:00
si458
0e3a6b4915 fix logoutOnIdleSessionTimeout spelling and extra translations #6851
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-03-08 16:48:37 +00:00
Ylian Saint-Hilaire
b949cecc5f Version 1.1.42 2025-03-07 09:06:50 -08:00
Ylian Saint-Hilaire
c7cbf2f12a Updated Windows x86-32 and 64 agents. 2025-03-07 09:05:07 -08:00
si458
d49afdd7bf report version/size correctly to control panel #6860
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-03-07 16:31:11 +00:00
Ylian Saint-Hilaire
133e77c8c6 Version 1.1.41 2025-03-05 18:09:42 -08:00
Ylian Saint-Hilaire
e404e86b9f Updated Windows Agents 2025-03-05 18:06:30 -08:00
si458
c6da201af8 fix duo 2fa redirect when session changes
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-03-05 10:37:39 +00:00
Roman
9a27d7637c
Improvements to the German translation in various areas (#6849) 2025-03-04 20:44:36 +00:00
Daniel Hammerschmidt
5aa2467409
add comment about meshrelay ping-pong in docs(#6842) 2025-03-04 18:47:00 +00:00
si458
9398afd07e fix getDeviceDetails asking for all devices not including lastconnect #6833
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-03-03 14:13:34 +00:00
Ylian Saint-Hilaire
b2cd84035b Version 1.1.40 2025-03-02 10:47:32 -08:00
Ylian Saint-Hilaire
97547d72a3 Updated MeshCmd 2025-03-02 10:46:13 -08:00
si458
7faf043c35 recording not recoding
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-27 18:35:11 +00:00
Daniel Hammerschmidt
9df0330896
avoid double logging in debug console (#6827) 2025-02-27 18:23:36 +00:00
si458
42f61ea46e fix bootstrap padding #6755
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-27 18:08:12 +00:00
ijustw0rkhere
0d65080a8a
fix runcommands in a peering environment (#6825)
* fix adding meshes and user groups in a peering environment

* fix runcommands in a peering environment
2025-02-27 15:12:18 +00:00
si458
bd4d8b12d4 fix consent with oldstyle: true and rdp sessions #6816
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-26 20:51:24 +00:00
si458
18ae8bdbf4 fix relaystate for entra #6822
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-26 20:32:38 +00:00
si458
46c76f7234 smoothing not smooting #6818
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-26 15:36:23 +00:00
Ylian Saint-Hilaire
e65bf77111 Version 1.1.39 2025-02-25 19:58:15 -08:00
Ylian Saint-Hilaire
f19ad6c664 Extra argument validation. 2025-02-25 19:57:27 -08:00
Ylian Saint-Hilaire
fe2f12149d Updated French Translations. 2025-02-25 19:20:43 -08:00
si458
91bd5ae702 fix minify files and log minify errors
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-25 17:21:50 +00:00
Daniel Hammerschmidt
38f5bf2e0f
Fix parameter names in usage message (#6810) 2025-02-25 13:08:51 +00:00
Daniel Hammerschmidt
79f00bcaab
fix evaluation of config.settings.agentlogdump (#6812) 2025-02-25 12:51:52 +00:00
si458
c55065505b fix relaylog ip order again
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-23 01:13:20 +00:00
PTR
e0e8a3fcaa
Add comma to escapeFieldName for NeDBv4 (#6803) 2025-02-22 16:59:41 +00:00
Daniel Hammerschmidt
3f77cfa93a
Add collectors to monitoring (#6777)
* Add collectors to monitoring

* Pass request and response objects to collectors
2025-02-20 22:37:14 +00:00
Jakub Maruszczak
5ee9aa2410
fix meshctrl tag filter search (#6798) 2025-02-20 22:16:15 +00:00
Jakub Maruszczak
0ab3f01ca6
prevent runcommand with --reply from terminating other ws connections (#6797) 2025-02-20 22:09:02 +00:00
Daniel Hammerschmidt
4b621a01fb
Fix gauge typo (#6778) 2025-02-20 21:59:36 +00:00
si458
f2681de87d fix webrtc viewonly mode #6792
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-19 14:03:31 +00:00
PTR
c90fa55c99
Init webstate with empty object (#6788) 2025-02-18 11:35:23 +00:00
si458
edeef03f00 track/show locked active users #6782
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-14 15:42:43 +00:00
si458
d7fe87d1db fix sessionrecording ip addresses wrong way round
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-13 21:39:15 +00:00
Martin Mädler
9d4f51e970
Add support for logoutOnIdleSessionTimeout (#6773) 2025-02-12 10:04:10 +00:00
si458
0b376fe5a0 fix player for windows and terminal #6761
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-11 17:04:19 +00:00
si458
9fd40751b2 fix remote desktop consent for rdp sessions #6710
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-09 21:41:08 +00:00
si458
d246307fae translation improvements
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-09 21:03:54 +00:00
Simon Smith
711bb56a93
autoAcceptIfNoUser (#6759)
* autoAcceptIfNoUser for desktop

* autoAcceptIfNoUser for terminal

* autoAcceptIfNoUser for files

* forgot few extra files

---------

Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-09 19:54:51 +00:00
Simon Smith
5734bcc33a
UI Toggle between Classic and Modern (#6763)
* [ENH] Add toggle switch for new MeshCentral UI in settings and top header of the classic UI

* [ENH] Add toggle for new MeshCentral UI in settings and in top headbar of the modern UI

* add showModernUIToggle and store uiviewmode in db

---------

Co-authored-by: kambereBr <brunokambere@gmail.com>
2025-02-09 19:41:47 +00:00
si458
1310c57397 return more than 100 groups for azure oidc #6669
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-05 21:21:39 +00:00
si458
854d6c00d2 fix view chunksize being too big causing pixelation
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-05 11:44:23 +00:00
si458
c96d7ff1ca remove CSP for web relay as apps dont load properly with the default set #6456
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-02-05 11:32:56 +00:00
Simon Smith
712f06db3c
fix meshcentral-config-schema.json for ace editor 2025-01-27 23:02:56 +00:00
si458
cac505e2cd few more translations
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-26 18:10:26 +00:00
interfect
9d962bc523
Note maximum password length in USB key dialog (#6735) 2025-01-26 18:02:42 +00:00
si458
f079692b16 change dnslytics.com to maclookup.app for mac address lookup #6704
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-26 14:57:41 +00:00
nmmclwhitehead
3ee06abfe8
Update webserver.js - allow saml relaystate in POST request (#6685)
added check for relaystate saml and regex check

added in rest of allowed params

correct formatting on regex string - now evaluates correctly

set relaystate on get request

check for ipv6
2025-01-26 14:42:48 +00:00
PTR
b46ddf2f70
Update console info command (#6722)
* Add human readable option (h) to info command

* Add option to helptext
2025-01-26 14:35:18 +00:00
PTR
f7b958d28b
Autobackup update (#6695)
* add backupHours option and many debug messages

* Cleanup debug messages, add backupinfo

* Add full path to remove log message

* Put backupcheck after config init, check proper backuppath

* Handle absolute backuppath, check access in checkBackupCapability, seperated expired files check to function, more message edits, serverwarnings

* Revert fallback to default backuppath

* Cleanup checkBackupCapability and messages

* add WebDAV messages
2025-01-26 14:24:40 +00:00
KevinBK1998
64c8d2c238
include username for amt direct connect in logs 2025-01-26 14:07:37 +00:00
si458
31f2224a93 add notransval to fix select options translate issues
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-26 13:54:04 +00:00
petervanv
de685556c8
Dutch language update 1.1.38 (#6716)
update and cleaned up some useless strings
2025-01-26 12:52:04 +00:00
si458
92375ddc93 add lastbootuptime to csv and fix spaces in csv translations #6723
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-26 12:42:30 +00:00
si458
ea80f8595e fix some functions not being called in sitestyle=3 #6733
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-26 10:59:08 +00:00
Ylian Saint-Hilaire
763f76b68f Version 1.1.38 2025-01-09 17:08:31 -08:00
nmmclwhitehead
1a02539f23
Added support for &gotodeviceip=x.x.x.x (#6672) 2025-01-08 11:27:50 +00:00
si458
90b71e924f properly fix multiple dialog popups in a row
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-07 16:52:19 +00:00
si458
73c18c4dd5 fix deviceaction multiple modals not working
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-07 16:35:13 +00:00
si458
def62075c7 fix theme switch to default bug and include google fonts in csp #6665
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-06 16:02:10 +00:00
Ylian Saint-Hilaire
998769a888 Version 1.1.37 2025-01-05 15:37:41 -08:00
si458
ca6ec5ebc9 fix delete node bootstrap ui
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-05 22:16:09 +00:00
si458
0dd56d5708 improve translations all around
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-05 21:37:56 +00:00
si458
95729d2a88 set default minify to false in docker
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-05 17:47:21 +00:00
si458
1ae7b9f641 fix minify in bootstrap #6662
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-05 17:38:03 +00:00
si458
c66a9a12ef i hate sundays, fix passport module again
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-05 14:48:51 +00:00
si458
2c310450cf fix passport module not installing for multiple domains
Signed-off-by: si458 <simonsmith5521@gmail.com>
2025-01-05 14:40:19 +00:00
81 changed files with 13657 additions and 14418 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -295,8 +295,9 @@ if (process.platform == 'win32' && require('user-sessions').isRoot()) {
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
try {
var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (ex) { }
if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (ex) { } }
var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent'));
try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize'); } catch (ex) { }
if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize', actualSize); } catch (ex) { } }
} catch (ex) { }
// Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
@ -310,6 +311,16 @@ if (process.platform == 'win32' && require('user-sessions').isRoot()) {
try { meshCheck = require('service-manager').manager.getService(svcname).isMe(); } catch (ex) { }
if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService(svcname)) { require('win-bcd').enableSafeModeService(svcname); }
} catch (ex) { }
// Check the Agent Uninstall MetaData for DisplayVersion and update if not the same and only on windows
if (process.platform == 'win32') {
try {
var writtenDisplayVersion = 0, actualDisplayVersion = process.versions.commitDate.toString();
var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent'));
try { writtenDisplayVersion = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'DisplayVersion'); } catch (ex) { }
if (writtenDisplayVersion != actualDisplayVersion) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'DisplayVersion', actualDisplayVersion); } catch (ex) { } }
} catch (ex) { }
}
}
if (process.platform != 'win32') {
@ -655,33 +666,39 @@ var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ?
try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (ex) { }
// Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
function onUserSessionChanged(user, locked) {
userSession.enumerateUsers().then(function (users) {
if (process.platform == 'linux') {
if (userSession._startTime == null) {
userSession._startTime = Date.now();
userSession._count = users.length;
}
else if (Date.now() - userSession._startTime < 10000 && users.length == userSession._count) {
userSession.removeAllListeners('changed');
return;
}
}
var u = [], a = users.Active;
if(meshCoreObj.lusers == null) { meshCoreObj.lusers = []; }
for (var i = 0; i < a.length; i++) {
var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
if (user && locked && (JSON.stringify(a[i]) === JSON.stringify(user))) { if (meshCoreObj.lusers.indexOf(un) == -1) { meshCoreObj.lusers.push(un); } }
else if (user && !locked && (JSON.stringify(a[i]) === JSON.stringify(user))) { meshCoreObj.lusers.splice(meshCoreObj.lusers.indexOf(un), 1); }
if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
}
meshCoreObj.lusers = meshCoreObj.lusers;
meshCoreObj.users = u;
meshCoreObjChanged();
});
}
try {
var userSession = require('user-sessions');
userSession.on('changed', function onUserSessionChanged() {
userSession.enumerateUsers().then(function (users) {
if (process.platform == 'linux') {
if (userSession._startTime == null) {
userSession._startTime = Date.now();
userSession._count = users.length;
}
else if (Date.now() - userSession._startTime < 10000 && users.length == userSession._count) {
userSession.removeAllListeners('changed');
return;
}
}
var u = [], a = users.Active;
for (var i = 0; i < a.length; i++) {
var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
}
meshCoreObj.users = u;
meshCoreObjChanged();
});
});
userSession.on('changed', function () { onUserSessionChanged(null, false); });
userSession.emit('changed');
//userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
userSession.on('locked', function (user) { if(user != undefined && user != null) { onUserSessionChanged(user, true); } });
userSession.on('unlocked', function (user) { if(user != undefined && user != null) { onUserSessionChanged(user, false); } });
} catch (ex) { }
var meshServerConnectionState = 0;
@ -1158,6 +1175,7 @@ function handleServerCommand(data) {
tunnel.soptions = data.soptions;
tunnel.consentTimeout = (tunnel.soptions && tunnel.soptions.consentTimeout) ? tunnel.soptions.consentTimeout : 30;
tunnel.consentAutoAccept = (tunnel.soptions && (tunnel.soptions.consentAutoAccept === true));
tunnel.consentAutoAcceptIfNoUser = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfNoUser === true));
tunnel.oldStyle = (tunnel.soptions && tunnel.soptions.oldStyle) ? tunnel.soptions.oldStyle : false;
tunnel.tcpaddr = data.tcpaddr;
tunnel.tcpport = data.tcpport;
@ -1572,7 +1590,7 @@ function handleServerCommand(data) {
mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
mesh.cmdchild.stdout.on('data', function (c) { replydata += c.toString(); });
mesh.cmdchild.stderr.on('data', function (c) { replydata + c.toString(); });
mesh.cmdchild.stderr.on('data', function (c) { replydata += c.toString(); });
mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
mesh.cmdchild.on('exit', function () {
if (data.reply) {
@ -2297,6 +2315,59 @@ function terminal_end()
}
function terminal_consent_ask(ws) {
ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['terminalConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
var consentTitle = 'MeshCentral';
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
if (ws.httprequest.soptions.consentMsgTerminal != null) { consentMessage = ws.httprequest.soptions.consentMsgTerminal.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
}
if (process.platform == 'win32') {
var enhanced = false;
if (ws.httprequest.oldStyle === false) {
try { require('win-userconsent'); enhanced = true; } catch (ex) { }
}
if (enhanced) {
var ipr = server_getUserImage(ws.httprequest.userid);
ipr.consentTitle = consentTitle;
ipr.consentMessage = consentMessage;
ipr.consentTimeout = ws.httprequest.consentTimeout;
ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
ipr.username = ws.httprequest.realname;
ipr.tsid = ws.tsid;
ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
ws.httprequest.tpromise._consent = ipr.then(function (img) {
this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
this.__childPromise.close = this.consent.close.bind(this.consent);
return (this.consent);
});
} else {
ws.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout);
}
} else {
ws.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout);
}
ws.httprequest.tpromise._consent.retPromise = ws.httprequest.tpromise;
ws.httprequest.tpromise._consent.then(function (always) {
if (always && process.platform == 'win32') { server_set_consentTimer(this.retPromise.httprequest.userid); }
// Success
MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
this.retPromise._consent = null;
this.retPromise._res();
}, function (e) {
if (this.retPromise.that) {
if(this.retPromise.that.httprequest){ // User Consent Denied
MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
} else { } // Connection was closed server side, maybe log some messages somewhere?
this.retPromise._consent = null;
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
} else { } // no websocket, maybe log some messages somewhere?
this.retPromise._rej(e.toString());
});
}
function terminal_promise_connection_rejected(e)
{
// FAILED to connect terminal
@ -2609,6 +2680,101 @@ function kvm_tunnel_consentpromise_closehandler()
if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }
}
function kvm_consent_ok(ws) {
// User Consent Prompt is not required because no user is present
if (ws.httprequest.consent && (ws.httprequest.consent & 1)){
// User Notifications is required
MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
var notifyMessage = currentTranslation['desktopNotify'].replace('{0}', ws.httprequest.realname);
var notifyTitle = "MeshCentral";
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
if (ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage, ws.tsid); } catch (ex) { }
} else {
MeshServerLogEx(36, null, "Started remote desktop without notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
}
if (ws.httprequest.consent && (ws.httprequest.consent & 0x40)) {
// Connection Bar is required
if (ws.httprequest.desktop.kvm.connectionBar) {
ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
ws.httprequest.desktop.kvm.connectionBar.close();
}
try {
ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(ws.httprequest.privacybartext.replace('{0}', ws.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
} catch (ex) {
MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or not Supported (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
}
if (ws.httprequest.desktop.kvm.connectionBar) {
ws.httprequest.desktop.kvm.connectionBar.state = {
userid: ws.httprequest.userid,
xuserid: ws.httprequest.xuserid,
username: ws.httprequest.username,
sessionid: ws.httprequest.sessionid,
remoteaddr: ws.httprequest.remoteaddr,
guestname: ws.httprequest.guestname,
desktop: ws.httprequest.desktop
};
ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
console.info1('Connection Bar Forcefully closed');
MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.state.remoteaddr + ")", this.state);
for (var i in this.state.desktop.kvm._pipedStreams) {
this.state.desktop.kvm._pipedStreams[i].end();
}
this.state.desktop.kvm.end();
});
}
}
ws.httprequest.desktop.kvm.pipe(ws, { dataTypeSkip: 1 });
if (ws.httprequest.autolock) {
destopLockHelper_pipe(ws.httprequest);
}
}
function kvm_consent_ask(ws){
// Send a console message back using the console channel, "\n" is supported.
ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['desktopConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
var consentTitle = 'MeshCentral';
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
if (ws.httprequest.soptions.consentMsgDesktop != null) { consentMessage = ws.httprequest.soptions.consentMsgDesktop.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
}
var pr;
if (process.platform == 'win32') {
var enhanced = false;
if (ws.httprequest.oldStyle === false) {
try { require('win-userconsent'); enhanced = true; } catch (ex) { }
}
if (enhanced) {
var ipr = server_getUserImage(ws.httprequest.userid);
ipr.consentTitle = consentTitle;
ipr.consentMessage = consentMessage;
ipr.consentTimeout = ws.httprequest.consentTimeout;
ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
ipr.tsid = ws.tsid;
ipr.username = ws.httprequest.realname;
ipr.translation = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
pr = ipr.then(function (img) {
this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translation, background: color_options.background, foreground: color_options.foreground });
this.__childPromise.close = this.consent.close.bind(this.consent);
return (this.consent);
});
} else {
pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null, ws.tsid);
}
} else {
pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null, ws.tsid);
}
pr.ws = ws;
ws.pause();
ws._consentpromise = pr;
ws.prependOnceListener('end', kvm_tunnel_consentpromise_closehandler);
pr.then(kvm_consentpromise_resolved, kvm_consentpromise_rejected);
}
function kvm_consentpromise_rejected(e)
{
if (this.ws) {
@ -2688,6 +2854,67 @@ function kvm_consentpromise_resolved(always)
this.ws = null;
}
function files_consent_ok(ws){
// User Consent Prompt is not required
if (ws.httprequest.consent && (ws.httprequest.consent & 4)) {
// User Notifications is required
MeshServerLogEx(42, null, "Started remote files with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
var notifyMessage = currentTranslation['fileNotify'].replace('{0}', ws.httprequest.realname);
var notifyTitle = "MeshCentral";
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
if (ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = ws.httprequest.soptions.notifyMsgFiles.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
} else {
MeshServerLogEx(43, null, "Started remote files without notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
}
ws.resume();
}
function files_consent_ask(ws){
// Send a console message back using the console channel, "\n" is supported.
ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['fileConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
var consentTitle = 'MeshCentral';
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
if (ws.httprequest.soptions.consentMsgFiles != null) { consentMessage = ws.httprequest.soptions.consentMsgFiles.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
}
var pr;
if (process.platform == 'win32') {
var enhanced = false;
if (ws.httprequest.oldStyle === false) {
try { require('win-userconsent'); enhanced = true; } catch (ex) { }
}
if (enhanced) {
var ipr = server_getUserImage(ws.httprequest.userid);
ipr.consentTitle = consentTitle;
ipr.consentMessage = consentMessage;
ipr.consentTimeout = ws.httprequest.consentTimeout;
ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
ipr.username = ws.httprequest.realname;
ipr.tsid = ws.tsid;
ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
pr = ipr.then(function (img) {
this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
this.__childPromise.close = this.consent.close.bind(this.consent);
return (this.consent);
});
} else {
pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null);
}
} else {
pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null);
}
pr.ws = ws;
ws.pause();
ws._consentpromise = pr;
ws.prependOnceListener('end', files_tunnel_endhandler);
pr.then(files_consentpromise_resolved, files_consentpromise_rejected);
}
function files_consentpromise_resolved(always)
{
if (always && process.platform == 'win32') { server_set_consentTimer(this.ws.httprequest.userid); }
@ -2801,6 +3028,12 @@ function onTunnelData(data)
this.descriptorMetadata = "Remote Terminal";
// Look for a TSID
var tsid = null;
if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
require('MeshAgent')._tsid = tsid;
this.tsid = tsid;
if (process.platform == 'win32')
{
if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9)) {
@ -2817,76 +3050,31 @@ function onTunnelData(data)
this.end = terminal_end;
// Perform User-Consent if needed.
if (this.httprequest.consent && (this.httprequest.consent & 16))
{
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['terminalConsent'].replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username);
var consentTitle = 'MeshCentral';
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
if (process.platform == 'win32')
{
var enhanced = false;
if (this.httprequest.oldStyle === false) {
try { require('win-userconsent'); enhanced = true; } catch (ex) { }
}
if (enhanced)
{
var ipr = server_getUserImage(this.httprequest.userid);
ipr.consentTitle = consentTitle;
ipr.consentMessage = consentMessage;
ipr.consentTimeout = this.httprequest.consentTimeout;
ipr.consentAutoAccept = this.httprequest.consentAutoAccept;
ipr.username = this.httprequest.realname;
ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
this.httprequest.tpromise._consent = ipr.then(function (img)
{
this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
this.__childPromise.close = this.consent.close.bind(this.consent);
return (this.consent);
});
} else
{
this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout);
}
} else
{
this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout);
}
this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
this.httprequest.tpromise._consent.then(
function (always)
{
if (always && process.platform == 'win32') { server_set_consentTimer(this.retPromise.httprequest.userid); }
// Success
MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
this.retPromise._consent = null;
this.retPromise._res();
},
function (e) {
if (this.retPromise.that) {
if(this.retPromise.that.httprequest){ // User Consent Denied
MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
} else { } // Connection was closed server side, maybe log some messages somewhere?
this.retPromise._consent = null;
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
} else { } // no websocket, maybe log some messages somewhere?
this.retPromise._rej(e.toString());
if (this.httprequest.consent && (this.httprequest.consent & 16)) {
// User asked for consent so now we check if we can auto accept if no user is present/loggedin
if (this.httprequest.consentAutoAcceptIfNoUser) {
var p = require('user-sessions').enumerateUsers();
p.sessionid = this.httprequest.sessionid;
p.ws = this;
p.then(function (u) {
var v = [];
for (var i in u) {
if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
}
if (v.length == 0) { // No user is present, auto accept
this.ws.httprequest.tpromise._res();
} else {
// User is present so we still need consent
terminal_consent_ask(this.ws);
}
});
}
else
{
} else {
terminal_consent_ask(this);
}
} else {
// User-Consent is not required, so just resolve this promise
this.httprequest.tpromise._res();
}
this.httprequest.tpromise.then(terminal_promise_consent_resolved, terminal_promise_consent_rejected);
}
else if (this.httprequest.protocol == 2)
@ -2910,6 +3098,7 @@ function onTunnelData(data)
var tsid = null;
if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
require('MeshAgent')._tsid = tsid;
this.tsid = tsid;
// If MacOS, Wake up device with caffeinate
if(process.platform == 'darwin'){
@ -2981,119 +3170,33 @@ function onTunnelData(data)
}
// Perform notification if needed. Toast messages may not be supported on all platforms.
if (this.httprequest.consent && (this.httprequest.consent & 8))
{
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['desktopConsent'].replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username);
var consentTitle = 'MeshCentral';
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
if (this.httprequest.consent && (this.httprequest.consent & 8)) {
// User asked for consent but now we check if can auto accept if no user is present
if (this.httprequest.consentAutoAcceptIfNoUser) {
// Get list of users to check if we any actual users logged in, and if users logged in, we still need consent
var p = require('user-sessions').enumerateUsers();
p.sessionid = this.httprequest.sessionid;
p.ws = this;
p.then(function (u) {
var v = [];
for (var i in u) {
if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
}
if (v.length == 0) { // No user is present, auto accept
kvm_consent_ok(this.ws);
} else {
// User is present so we still need consent
kvm_consent_ask(this.ws);
}
});
} else {
// User Consent Prompt is required
kvm_consent_ask(this);
}
var pr;
if (process.platform == 'win32')
{
var enhanced = false;
if (this.httprequest.oldStyle === false) {
try { require('win-userconsent'); enhanced = true; } catch (ex) { }
}
if (enhanced)
{
var ipr = server_getUserImage(this.httprequest.userid);
ipr.consentTitle = consentTitle;
ipr.consentMessage = consentMessage;
ipr.consentTimeout = this.httprequest.consentTimeout;
ipr.consentAutoAccept = this.httprequest.consentAutoAccept;
ipr.tsid = tsid;
ipr.username = this.httprequest.realname;
ipr.translation = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
pr = ipr.then(function (img)
{
this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translation, background: color_options.background, foreground: color_options.foreground });
this.__childPromise.close = this.consent.close.bind(this.consent);
return (this.consent);
});
}
else
{
pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null, tsid);
}
}
else
{
pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null, tsid);
}
pr.ws = this;
this.pause();
this._consentpromise = pr;
this.prependOnceListener('end', kvm_tunnel_consentpromise_closehandler);
pr.then(kvm_consentpromise_resolved, kvm_consentpromise_rejected);
}
else
{
} else {
// User Consent Prompt is not required
if (this.httprequest.consent && (this.httprequest.consent & 1))
{
// User Notifications is required
MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
var notifyMessage = currentTranslation['desktopNotify'].replace('{0}', this.httprequest.realname);
var notifyTitle = "MeshCentral";
if (this.httprequest.soptions != null) {
if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
} else
{
MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
}
if (this.httprequest.consent && (this.httprequest.consent & 0x40))
{
// Connection Bar is required
if (this.httprequest.desktop.kvm.connectionBar)
{
this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
this.httprequest.desktop.kvm.connectionBar.close();
}
try
{
this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
} catch (ex) {
MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
}
if (this.httprequest.desktop.kvm.connectionBar)
{
this.httprequest.desktop.kvm.connectionBar.state =
{
userid: this.httprequest.userid,
xuserid: this.httprequest.xuserid,
username: this.httprequest.username,
sessionid: this.httprequest.sessionid,
remoteaddr: this.httprequest.remoteaddr,
guestname: this.httprequest.guestname,
desktop: this.httprequest.desktop
};
this.httprequest.desktop.kvm.connectionBar.on('close', function ()
{
console.info1('Connection Bar Forcefully closed');
MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.state.remoteaddr + ")", this.state);
for (var i in this.state.desktop.kvm._pipedStreams)
{
this.state.desktop.kvm._pipedStreams[i].end();
}
this.state.desktop.kvm.end();
});
}
}
this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
if (this.httprequest.autolock)
{
destopLockHelper_pipe(this.httprequest);
}
kvm_consent_ok(this);
}
this.removeAllListeners('data');
@ -3115,6 +3218,12 @@ function onTunnelData(data)
this.descriptorMetadata = "Remote Files";
// Look for a TSID
var tsid = null;
if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
require('MeshAgent')._tsid = tsid;
this.tsid = tsid;
// Add the files session to the count to update the server
if (this.httprequest.userid != null) {
var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest);
@ -3137,71 +3246,31 @@ function onTunnelData(data)
// Perform notification if needed. Toast messages may not be supported on all platforms.
if (this.httprequest.consent && (this.httprequest.consent & 32))
{
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['fileConsent'].replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username);
var consentTitle = 'MeshCentral';
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
var pr;
if (process.platform == 'win32')
{
var enhanced = false;
if (this.httprequest.oldStyle === false) {
try { require('win-userconsent'); enhanced = true; } catch (ex) { }
}
if (enhanced)
{
var ipr = server_getUserImage(this.httprequest.userid);
ipr.consentTitle = consentTitle;
ipr.consentMessage = consentMessage;
ipr.consentTimeout = this.httprequest.consentTimeout;
ipr.consentAutoAccept = this.httprequest.consentAutoAccept;
ipr.username = this.httprequest.realname;
ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
pr = ipr.then(function (img)
{
this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
this.__childPromise.close = this.consent.close.bind(this.consent);
return (this.consent);
});
} else
{
pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null);
}
}
else
{
pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null);
}
pr.ws = this;
this.pause();
this._consentpromise = pr;
this.prependOnceListener('end', files_tunnel_endhandler);
pr.then(files_consentpromise_resolved, files_consentpromise_rejected);
}
else
{
// User Consent Prompt is not required
if (this.httprequest.consent && (this.httprequest.consent & 4)) {
// User Notifications is required
MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
var notifyMessage = currentTranslation['fileNotify'].replace('{0}', this.httprequest.realname);
var notifyTitle = "MeshCentral";
if (this.httprequest.soptions != null) {
if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
// User asked for consent so now we check if we can auto accept if no user is present/loggedin
if (this.httprequest.consentAutoAcceptIfNoUser) {
var p = require('user-sessions').enumerateUsers();
p.sessionid = this.httprequest.sessionid;
p.ws = this;
p.then(function (u) {
var v = [];
for (var i in u) {
if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
}
if (v.length == 0) { // No user is present, auto accept
// User Consent Prompt is not required
files_consent_ok(this.ws);
} else {
// User is present so we still need consent
files_consent_ask(this.ws);
}
});
} else {
MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
// User Consent Prompt is required
files_consent_ask(this);
}
this.resume();
} else {
// User Consent Prompt is not required
files_consent_ok(this);
}
// Setup files
@ -3689,7 +3758,14 @@ function onTunnelControlData(data, ws) {
{ // Desktop
// Switch the user input from websocket to webrtc at this point.
ws.unpipe(ws.httprequest.desktop.kvm);
try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (ex) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
if ((ws.httprequest.desktopviewonly != true) && ((ws.httprequest.rights == 0xFFFFFFFF) || (((ws.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((ws.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)))) {
// If we have remote control rights, pipe the KVM input
try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (ex) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
} else {
// We need to only pipe non-mouse & non-keyboard inputs.
// sendConsoleText('Warning: No Remote Desktop Input Rights.');
// TODO!!!
}
ws.resume(); // Resume the websocket to keep receiving control data
}
ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
@ -4290,7 +4366,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
}
case 'agentmsg': {
if (args['_'].length == 0) {
response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [id]\r\n agentmsg list"; // Display usage
} else {
if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
var msgID, iconIndex = 0;
@ -4508,10 +4584,11 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
if (process.platform == 'win32') {
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
var writtenSize = 0;
try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (ex) { response = ex; }
var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent'));
try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize'); } catch (ex) { response = ex; }
if (writtenSize != actualSize) {
response = "Size updated from: " + writtenSize + " to: " + actualSize;
try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (ex) { response = ex; }
try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize', actualSize); } catch (ex) { response = ex; }
} else
{ response = "Agent Size: " + actualSize + " kb"; }
} else
@ -5591,7 +5668,7 @@ function windows_execve(name, agentfilename, sessionid) {
sendAgentMessage('Self Update failed because msvcrt.dll is missing', 3);
return;
}
var cmd = require('_GenericMarshal').CreateVariable(process.env['windir'] + '\\system32\\cmd.exe', { wide: true });
var args = require('_GenericMarshal').CreateVariable(3 * require('_GenericMarshal').PointerSize);
var arg1 = require('_GenericMarshal').CreateVariable('cmd.exe', { wide: true });

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -719,7 +719,8 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
}
else if (blockHeaders.indexOf(i) == -1) { obj.res.set(i.trim(), header[i]); } // Set the headers if not blocked
}
obj.res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;"); // Set an "allow all" policy, see if the can restrict this in the future
// Dont set any Content-Security-Policy at all because some applications like Node-Red, access external websites from there javascript which would be forbidden by the below CSP
//obj.res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;"); // Set an "allow all" policy, see if the can restrict this in the future
//obj.res.set('Content-Security-Policy', "default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';"); // Set an "allow all" policy, see if the can restrict this in the future
obj.res.set('Cache-Control', 'no-store'); // Tell the browser not to cache the responses since since the relay port can be used for many relays
}

View file

@ -155,12 +155,12 @@ module.exports.objKeysToLower = function (obj, exceptions, parent) {
return obj;
};
// Escape and unescape field names so there are no invalid characters for MongoDB
module.exports.escapeFieldName = function (name) { if ((name.indexOf('%') == -1) && (name.indexOf('.') == -1) && (name.indexOf('$') == -1)) return name; return name.split('%').join('%25').split('.').join('%2E').split('$').join('%24'); };
module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2E').join('.').split('%24').join('$').split('%25').join('%'); };
// Escape and unescape field names so there are no invalid characters for MongoDB/NeDB ("$", ",", ".", see https://github.com/seald/nedb/tree/master?tab=readme-ov-file#inserting-documents)
module.exports.escapeFieldName = function (name) { if ((name.indexOf(',') == -1) && (name.indexOf('%') == -1) && (name.indexOf('.') == -1) && (name.indexOf('$') == -1)) return name; return name.split('%').join('%25').split('.').join('%2E').split('$').join('%24').split(',').join('%2C'); };
module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2C').join(',').split('%2E').join('.').split('%24').join('$').split('%25').join('%'); };
// Escape all links, SSH and RDP usernames
// This is required for databases like NeDB that don't accept "." as part of a field name.
// This is required for databases like NeDB that don't accept "." or "," as part of a field name.
module.exports.escapeLinksFieldNameEx = function (docx) { if ((docx.links == null) && (docx.ssh == null) && (docx.rdp == null)) { return docx; } return module.exports.escapeLinksFieldName(docx); };
module.exports.escapeLinksFieldName = function (docx) {
var doc = Object.assign({}, docx);

298
db.js
View file

@ -781,10 +781,10 @@ module.exports.CreateDB = function (parent, func) {
parent.debug('db', 'SQlite config options: ' + JSON.stringify(obj.sqliteConfig, null, 4));
if (obj.sqliteConfig.journalMode == 'memory') { console.log('[WARNING] journal_mode=memory: this can lead to database corruption if there is a crash during a transaction. See https://www.sqlite.org/pragma.html#pragma_journal_mode') };
//.cached not usefull
obj.file = new sqlite3.Database(parent.path.join(parent.datapath, databaseName + '.sqlite'), sqlite3.OPEN_READWRITE, function (err) {
obj.file = new sqlite3.Database(path.join(parent.datapath, databaseName + '.sqlite'), sqlite3.OPEN_READWRITE, function (err) {
if (err && (err.code == 'SQLITE_CANTOPEN')) {
// Database needs to be created
obj.file = new sqlite3.Database(parent.path.join(parent.datapath, databaseName + '.sqlite'), function (err) {
obj.file = new sqlite3.Database(path.join(parent.datapath, databaseName + '.sqlite'), function (err) {
if (err) { console.log("SQLite Error: " + err); process.exit(1); }
obj.file.exec(`
CREATE TABLE main (id VARCHAR(256) PRIMARY KEY NOT NULL, type CHAR(32), domain CHAR(64), extra CHAR(255), extraex CHAR(255), doc JSON);
@ -975,7 +975,7 @@ module.exports.CreateDB = function (parent, func) {
} else {
if ((info.versionArray[0] < 3) || ((info.versionArray[0] == 3) && (info.versionArray[1] < 6))) {
// We are running with mongoDB older than 3.6, this is not good.
parent.addServerWarning("Current version of MongoDB (" + info.version + ") is too old, please upgrade to MongoDB 3.6 or better.");
parent.addServerWarning("Current version of MongoDB (" + info.version + ") is too old, please upgrade to MongoDB 3.6 or better.", true);
}
}
});
@ -1294,7 +1294,7 @@ module.exports.CreateDB = function (parent, func) {
// Setup the SMBIOS collection, for NeDB we don't setup SMBIOS since NeDB will corrupt the database. Remove any existing ones.
//obj.smbiosfile = new Datastore({ filename: parent.getConfigFilePath('meshcentral-smbios.db'), autoload: true, corruptAlertThreshold: 1 });
parent.fs.unlink(parent.getConfigFilePath('meshcentral-smbios.db'), function () { });
fs.unlink(parent.getConfigFilePath('meshcentral-smbios.db'), function () { });
// Setup the server stats collection and setup indexes
obj.serverstatsfile = new Datastore({ filename: parent.getConfigFilePath('meshcentral-stats.db'), autoload: true, corruptAlertThreshold: 1 });
@ -3187,7 +3187,6 @@ module.exports.CreateDB = function (parent, func) {
// Return a human readable string with current backup configuration
obj.getBackupConfig = function () {
var r = '', backupPath = parent.backuppath;
if (parent.config.settings.autobackup && parent.config.settings.autobackup.backuppath) { backupPath = parent.config.settings.autobackup.backuppath; }
let dbname = 'meshcentral';
if (parent.args.mongodbname) { dbname = parent.args.mongodbname; }
@ -3197,7 +3196,7 @@ module.exports.CreateDB = function (parent, func) {
const currentDate = new Date();
const fileSuffix = currentDate.getFullYear() + '-' + padNumber(currentDate.getMonth() + 1, 2) + '-' + padNumber(currentDate.getDate(), 2) + '-' + padNumber(currentDate.getHours(), 2) + '-' + padNumber(currentDate.getMinutes(), 2);
obj.newAutoBackupFile = ((typeof parent.config.settings.autobackup.backupname == 'string') ? parent.config.settings.autobackup.backupname : 'meshcentral-autobackup-') + fileSuffix;
obj.newAutoBackupFile = parent.config.settings.autobackup.backupname + fileSuffix;
r += 'DB Name: ' + dbname + '\r\n';
r += 'DB Type: ' + DB_LIST[obj.databaseType] + '\r\n';
@ -3207,15 +3206,14 @@ module.exports.CreateDB = function (parent, func) {
if (parent.config.settings.autobackup == null) {
r += 'No Settings/AutoBackup\r\n';
} else {
if (parent.config.settings.autobackup.backuphour != null && parent.config.settings.autobackup.backuphour != -1) {
r += 'Backup between: ' + parent.config.settings.autobackup.backuphour + 'H-' + (parent.config.settings.autobackup.backuphour + 1) + 'H\r\n';
}
if (parent.config.settings.autobackup.backupintervalhours != null) {
r += 'Backup Interval (Hours): ';
if (typeof parent.config.settings.autobackup.backupintervalhours != 'number') { r += 'Bad backupintervalhours type\r\n'; }
else { r += parent.config.settings.autobackup.backupintervalhours + '\r\n'; }
r += 'Backup Interval (Hours): ' + parent.config.settings.autobackup.backupintervalhours + '\r\n';
}
if (parent.config.settings.autobackup.keeplastdaysbackup != null) {
r += 'Keep Last Backups (Days): ';
if (typeof parent.config.settings.autobackup.keeplastdaysbackup != 'number') { r += 'Bad keeplastdaysbackup type\r\n'; }
else { r += parent.config.settings.autobackup.keeplastdaysbackup + '\r\n'; }
r += 'Keep Last Backups (Days): ' + parent.config.settings.autobackup.keeplastdaysbackup + '\r\n';
}
if (parent.config.settings.autobackup.zippassword != null) {
r += 'ZIP Password: ';
@ -3330,48 +3328,70 @@ module.exports.CreateDB = function (parent, func) {
}
// Check that the server is capable of performing a backup
// Tries configured custom location with fallback to default location
// Now runs after autobackup config init in meshcentral.js so config options are checked
obj.checkBackupCapability = function (func) {
if ((parent.config.settings.autobackup == null) || (parent.config.settings.autobackup == false)) { func(); return; };
if ((parent.config.settings.autobackup == null) || (parent.config.settings.autobackup == false)) { return; };
//block backup until validated. Gets put back if all checks are ok.
let backupInterval = parent.config.settings.autobackup.backupintervalhours;
parent.config.settings.autobackup.backupintervalhours = -1;
let backupPath = parent.backuppath;
if (parent.config.settings.autobackup && parent.config.settings.autobackup.backuppath) { backupPath = parent.config.settings.autobackup.backuppath; }
try { parent.fs.mkdirSync(backupPath); } catch (e) { }
if (parent.fs.existsSync(backupPath) == false) { func(1, "Backup folder \"" + backupPath + "\" does not exist, auto-backup will not be performed."); return; }
if (backupPath.startsWith(parent.datapath)) {
func(1, "Backup path can't be set within meshcentral-data folder. No backups will be made.");
return;
}
// Check create/write backupdir
try { fs.mkdirSync(backupPath); }
catch (e) {
// EEXIST error = dir already exists
if (e.code != 'EEXIST' ) {
//Unable to create backuppath
console.error(e.message);
func(1, 'Unable to create ' + backupPath + '. No backups will be made. Error: ' + e.message);
return;
}
}
const testFile = path.join(backupPath, (parent.config.settings.autobackup.backupname + ".test"));
try { fs.writeFileSync( testFile, "DeleteMe"); }
catch (e) {
//Unable to create file
console.error (e.message);
func(1, "Backuppath (" + backupPath + ") can't be written to. No backups will be made. Error: " + e.message);
return;
}
try { fs.unlinkSync(testFile); parent.debug('backup', 'Backuppath ' + backupPath + ' accesscheck successful');}
catch (e) {
console.error (e.message);
func(1, "Backuppathtestfile (" + testFile + ") can't be deleted, check filerights. Error: " + e.message);
// Assume write rights, no delete rights. Continue with warning.
//return;
}
// Check database dumptools
if ((obj.databaseType == DB_MONGOJS) || (obj.databaseType == DB_MONGODB)) {
// Check that we have access to MongoDump
var cmd = buildMongoDumpCommand();
cmd += (parent.platform == 'win32') ? ' --archive=\"nul\"' : ' --archive=\"/dev/null\"';
const child_process = require('child_process');
child_process.exec(cmd, { cwd: backupPath }, function (error, stdout, stderr) {
try {
if ((error != null) && (error != '')) {
if (parent.platform == 'win32') {
func(1, "Unable to find mongodump.exe, MongoDB database auto-backup will not be performed.");
} else {
func(1, "Unable to find mongodump, MongoDB database auto-backup will not be performed.");
}
} else {
func();
}
} catch (ex) { console.log(ex); }
if ((error != null) && (error != '')) {
func(1, "Unable to find mongodump tool, backup will not be performed. Command tried: " + cmd);
return;
} else {parent.config.settings.autobackup.backupintervalhours = backupInterval;}
});
} else if ((obj.databaseType == DB_MARIADB) || (obj.databaseType == DB_MYSQL)) {
// Check that we have access to mysqldump
var cmd = buildSqlDumpCommand();
cmd += ' > ' + ((parent.platform == 'win32') ? '\"nul\"' : '\"/dev/null\"');
const child_process = require('child_process');
child_process.exec(cmd, { cwd: backupPath }, function(error, stdout, stdin) {
try {
if ((error != null) && (error != '')) {
if (parent.platform == 'win32') {
func(1, "Unable to find mysqldump.exe, MySQL/MariaDB database auto-backup will not be performed.");
} else {
func(1, "Unable to find mysqldump, MySQL/MariaDB database auto-backup will not be performed.");
}
} else {
func();
}
} catch (ex) { console.log(ex); }
child_process.exec(cmd, { cwd: backupPath, timeout: 1000*30 }, function(error, stdout, stdin) {
if ((error != null) && (error != '')) {
func(1, "Unable to find mysqldump tool, backup will not be performed. Command tried: " + cmd);
return;
} else {parent.config.settings.autobackup.backupintervalhours = backupInterval;}
});
} else if (obj.databaseType == DB_POSTGRESQL) {
// Check that we have access to pg_dump
@ -3382,17 +3402,14 @@ module.exports.CreateDB = function (parent, func) {
+ ' > ' + ((parent.platform == 'win32') ? '\"nul\"' : '\"/dev/null\"');
const child_process = require('child_process');
child_process.exec(cmd, { cwd: backupPath }, function(error, stdout, stdin) {
try {
if ((error != null) && (error != '')) {
func(1, "Unable to find pg_dump, PostgreSQL database auto-backup will not be performed.");
} else {
func();
}
} catch (ex) { console.log(ex); }
if ((error != null) && (error != '')) {
func(1, "Unable to find pg_dump tool, backup will not be performed. Command tried: " + cmd);
return;
} else {parent.config.settings.autobackup.backupintervalhours = backupInterval;}
});
} else {
func();
}
//all ok, enable backup
parent.config.settings.autobackup.backupintervalhours = backupInterval;}
}
// MongoDB pending bulk read operation, perform fast bulk document reads.
@ -3506,19 +3523,18 @@ module.exports.CreateDB = function (parent, func) {
// Perform a server backup
obj.performBackup = function (func) {
parent.debug('db','Entering performBackup');
parent.debug('backup','Entering performBackup');
try {
if (obj.performingBackup) return 'Backup alreay in progress.';
if (parent.config.settings.autobackup.backupintervalhours == -1) { if (func) { func('Unable to create backup if backuppath is set to the data folder.'); return 'Backup aborted.' }};
if (parent.config.settings.autobackup.backupintervalhours == -1) { if (func) { func('Backup disabled.'); return 'Backup disabled.' }};
obj.performingBackup = true;
let backupPath = parent.backuppath;
let dataPath = parent.datapath;
if (parent.config.settings.autobackup && parent.config.settings.autobackup.backuppath) { backupPath = parent.config.settings.autobackup.backuppath; }
try { parent.fs.mkdirSync(backupPath); } catch (e) { }
const currentDate = new Date();
const fileSuffix = currentDate.getFullYear() + '-' + padNumber(currentDate.getMonth() + 1, 2) + '-' + padNumber(currentDate.getDate(), 2) + '-' + padNumber(currentDate.getHours(), 2) + '-' + padNumber(currentDate.getMinutes(), 2);
obj.newAutoBackupFile = path.join(backupPath, ((typeof parent.config.settings.autobackup.backupname == 'string') ? parent.config.settings.autobackup.backupname : 'meshcentral-autobackup-') + fileSuffix + '.zip');
obj.newAutoBackupFile = path.join(backupPath, parent.config.settings.autobackup.backupname + fileSuffix + '.zip');
parent.debug('backup','newAutoBackupFile=' + obj.newAutoBackupFile);
if ((obj.databaseType == DB_MONGOJS) || (obj.databaseType == DB_MONGODB)) {
// Perform a MongoDump
@ -3530,13 +3546,14 @@ module.exports.CreateDB = function (parent, func) {
var cmd = buildMongoDumpCommand();
cmd += (dburl) ? ' --archive=\"' + obj.newDBDumpFile + '\"' :
' --db=\"' + dbname + '\" --archive=\"' + obj.newDBDumpFile + '\"';
parent.debug('backup','Mongodump cmd: ' + cmd);
const child_process = require('child_process');
const dumpProcess = child_process.exec(
cmd,
{ cwd: parent.parentpath },
(error)=> {if (error) {obj.backupStatus |= BACKUPFAIL_DBDUMP; console.log('ERROR: Unable to perform MongoDB backup: ' + error + '\r\n'); obj.createBackupfile(func);}}
(error)=> {if (error) {obj.backupStatus |= BACKUPFAIL_DBDUMP; console.error('ERROR: Unable to perform MongoDB backup: ' + error + '\r\n'); obj.createBackupfile(func);}}
);
dumpProcess.on('exit', (code) => {
if (code != 0) {console.log(`Mongodump child process exited with code ${code}`); obj.backupStatus |= BACKUPFAIL_DBDUMP;}
obj.createBackupfile(func);
@ -3549,15 +3566,16 @@ module.exports.CreateDB = function (parent, func) {
var cmd = buildSqlDumpCommand();
cmd += ' --result-file=\"' + obj.newDBDumpFile + '\"';
parent.debug('backup','Maria/MySQLdump cmd: ' + cmd);
const child_process = require('child_process');
const dumpProcess = child_process.exec(
cmd,
{ cwd: parent.parentpath },
(error)=> {if (error) {obj.backupStatus |= BACKUPFAIL_DBDUMP; console.log('ERROR: Unable to perform MySQL backup: ' + error + '\r\n'); obj.createBackupfile(func);}}
(error)=> {if (error) {obj.backupStatus |= BACKUPFAIL_DBDUMP; console.error('ERROR: Unable to perform MySQL backup: ' + error + '\r\n'); obj.createBackupfile(func);}}
);
dumpProcess.on('exit', (code) => {
if (code != 0) {console.log(`MySQLdump child process exited with code ${code}`); obj.backupStatus |= BACKUPFAIL_DBDUMP;}
if (code != 0) {console.error(`MySQLdump child process exited with code ${code}`); obj.backupStatus |= BACKUPFAIL_DBDUMP;}
obj.createBackupfile(func);
});
@ -3565,8 +3583,9 @@ module.exports.CreateDB = function (parent, func) {
//.db3 suffix to escape escape backupfile glob to exclude the sqlite db files
obj.newDBDumpFile = path.join(backupPath, databaseName + '-sqlitedump-' + fileSuffix + '.db3');
// do a VACUUM INTO in favor of the backup API to compress the export, see https://www.sqlite.org/backup.html
parent.debug('backup','SQLitedump: VACUUM INTO ' + obj.newDBDumpFile);
obj.file.exec('VACUUM INTO \'' + obj.newDBDumpFile + '\'', function (err) {
if (err) { console.log('SQLite start-backup error: ' + err); obj.backupStatus |=BACKUPFAIL_DBDUMP;};
if (err) { console.error('SQLite backup error: ' + err); obj.backupStatus |=BACKUPFAIL_DBDUMP;};
//always finish/clean up
obj.createBackupfile(func);
});
@ -3578,6 +3597,7 @@ module.exports.CreateDB = function (parent, func) {
+ ' --dbname=postgresql://' + parent.config.settings.postgres.user + ":" +parent.config.settings.postgres.password
+ "@" + parent.config.settings.postgres.host + ":" + parent.config.settings.postgres.port + "/" + databaseName
+ " --file=" + obj.newDBDumpFile;
parent.debug('backup','Postgresqldump cmd: ' + cmd);
const child_process = require('child_process');
const dumpProcess = child_process.exec(
cmd,
@ -3589,15 +3609,15 @@ module.exports.CreateDB = function (parent, func) {
obj.createBackupfile(func);
});
} else {
//NeDB backup, no db dump needed, just make a file backup
// NeDB/Acebase backup, no db dump needed, just make a file backup
obj.createBackupfile(func);
}
} catch (ex) { console.log(ex); };
} catch (ex) { console.error(ex); parent.addServerWarning( 'Something went wrong during performBackup, check errorlog: ' +ex.message, true); };
return 'Starting auto-backup...';
};
obj.createBackupfile = function(func) {
parent.debug('db', 'Entering createFileBackup');
parent.debug('backup', 'Entering createBackupfile');
let archiver = require('archiver');
let archive = null;
let zipLevel = Math.min(Math.max(Number(parent.config.settings.autobackup.zipcompression ? parent.config.settings.autobackup.zipcompression : 5),1),9);
@ -3611,8 +3631,8 @@ module.exports.CreateDB = function (parent, func) {
if (func) { func('Creating encrypted ZIP'); }
} catch (ex) { // registering encryption failed, do not fall back to non-encrypted, fail backup and skip old backup removal as a precaution to not lose any backups
obj.backupStatus |= BACKUPFAIL_ZIPMODULE;
if (func) { func('Zipencryptionmodule failed, aborting'); }
console.log('Zipencryptionmodule failed, aborting');
if (func) { func('Zipencryptionmodule failed, aborting');}
console.error('Zipencryptionmodule failed, aborting');
}
} else {
if (func) { func('Creating a NON-ENCRYPTED ZIP'); }
@ -3622,51 +3642,36 @@ module.exports.CreateDB = function (parent, func) {
//original behavior, just a filebackup if dbdump fails : (obj.backupStatus == 0 || obj.backupStatus == BACKUPFAIL_DBDUMP)
if (obj.backupStatus == 0) {
// Zip the data directory with the dbdump|NeDB files
let output = parent.fs.createWriteStream(obj.newAutoBackupFile);
let output = fs.createWriteStream(obj.newAutoBackupFile);
// Archive finalized and closed
output.on('close', function () {
if (obj.backupStatus == 0) {
//remove dump archive file, because zipped and otherwise fills up
if (obj.databaseType != DB_NEDB) {
try { parent.fs.unlink(obj.newDBDumpFile, function () { }); } catch (ex) {console.log('Failed to clean up dbdump file')};
};
let mesg = 'Auto-backup completed: ' + obj.newAutoBackupFile + ', backup-size: ' + ((archive.pointer() / 1048576).toFixed(2)) + "Mb";
console.log(mesg);
if (func) { func(mesg); };
obj.performCloudBackup(obj.newAutoBackupFile, func);
// Remove old backups
if (parent.config.settings.autobackup && (typeof parent.config.settings.autobackup.keeplastdaysbackup == 'number')) {
let cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - parent.config.settings.autobackup.keeplastdaysbackup);
parent.fs.readdir(parent.backuppath, function (err, dir) {
try {
if ((err == null) && (dir.length > 0)) {
let fileName = (typeof parent.config.settings.autobackup.backupname == 'string') ? parent.config.settings.autobackup.backupname : 'meshcentral-autobackup-';
for (var i in dir) {
var name = dir[i];
if (name.startsWith(fileName) && name.endsWith('.zip')) {
var timex = name.substring(23, name.length - 4).split('-');
if (timex.length == 5) {
var fileDate = new Date(parseInt(timex[0]), parseInt(timex[1]) - 1, parseInt(timex[2]), parseInt(timex[3]), parseInt(timex[4]));
if (fileDate && (cutoffDate > fileDate)) { try { parent.fs.unlink(parent.path.join(parent.backuppath, name), function () { }); } catch (ex) { } }
}
}
}
}
} catch (ex) { console.log(ex); }
});
}
console.log('Auto-backup completed.');
if (func) { func('Auto-backup completed.'); };
obj.removeExpiredBackupfiles(func);
} else {
console.log('Zipbackup failed ('+ (+obj.backupStatus).toString(16).slice(-4) + '), deleting incomplete backup: ' + obj.newAutoBackupFile );
if (func) { func('Zipbackup failed ('+ (+obj.backupStatus).toString(16).slice(-4) + '), deleting incomplete backup: ' + obj.newAutoBackupFile) };
try { parent.fs.unlink(obj.newAutoBackupFile, function () { }); parent.fs.unlink(obj.newDBDumpFile, function () { }); } catch (ex) {console.log('Failed to delete incomplete backup files')};
let mesg = 'Zipbackup failed (' + obj.backupStatus.toString(2).slice(-8) + '), deleting incomplete backup: ' + obj.newAutoBackupFile;
if (func) { func(mesg) }
else { parent.addServerWarning(mesg, true ) };
if (fs.existsSync(obj.newAutoBackupFile)) { fs.unlink(obj.newAutoBackupFile, function (err) { console.error('Failed to clean up backupfile: ' + err.message) }) };
};
if (obj.databaseType != DB_NEDB) {
//remove dump archive file, because zipped and otherwise fills up
if (fs.existsSync(obj.newDBDumpFile)) { fs.unlink(obj.newDBDumpFile, function (err) { if (err) {console.error('Failed to clean up dbdump file: ' + err.message) } }) };
};
obj.performingBackup = false;
obj.backupStatus = 0x0;
});
}
);
output.on('end', function () { });
output.on('error', function (err) {
if ((obj.backupStatus & BACKUPFAIL_ZIPCREATE) == 0) {
console.log('Output error: ' + err);
if (func) { func('Output error: ' + err); };
console.error('Output error: ' + err.message);
if (func) { func('Output error: ' + err.message); };
obj.backupStatus |= BACKUPFAIL_ZIPCREATE;
archive.abort();
};
@ -3676,16 +3681,16 @@ module.exports.CreateDB = function (parent, func) {
//an ENOENT warning is given, but the archiver module has no option to/does not skip/resume
//so the backup needs te be aborted as it otherwise leaves an incomplete zip and never 'ends'
if ((obj.backupStatus & BACKUPFAIL_ZIPCREATE) == 0) {
console.log('Zip warning: ' + err);
if (func) { func('Zip warning: ' + err); };
console.log('Zip warning: ' + err.message);
if (func) { func('Zip warning: ' + err.message); };
obj.backupStatus |= BACKUPFAIL_ZIPCREATE;
archive.abort();
};
});
archive.on('error', function (err) {
if ((obj.backupStatus & BACKUPFAIL_ZIPCREATE) == 0) {
console.log('Zip error: ' + err);
if (func) { func('Zip error: ' + err); };
console.error('Zip error: ' + err.message);
if (func) { func('Zip error: ' + err.message); };
obj.backupStatus |= BACKUPFAIL_ZIPCREATE;
archive.abort();
}
@ -3718,22 +3723,67 @@ module.exports.CreateDB = function (parent, func) {
archive.finalize();
} else {
//failed somewhere before zipping
console.log('Backup failed ('+ (+obj.backupStatus).toString(16).slice(-4) + ')');
if (func) { func('Backup failed ('+ (+obj.backupStatus).toString(16).slice(-4) + ')') };
console.error('Backup failed ('+ obj.backupStatus.toString(2).slice(-8) + ')');
if (func) { func('Backup failed ('+ obj.backupStatus.toString(2).slice(-8) + ')') }
else {
parent.addServerWarning('Backup failed ('+ obj.backupStatus.toString(2).slice(-8) + ')', true);
}
//Just in case something's there
try { parent.fs.unlink(obj.newDBDumpFile, function () { }); } catch (ex) { };
if (fs.existsSync(obj.newDBDumpFile)) { fs.unlink(obj.newDBDumpFile, function (err) { if (err) {console.error('Failed to clean up dbdump file: ' + err.message) } }); };
obj.backupStatus = 0x0;
obj.performingBackup = false;
};
};
// Remove expired backupfiles by filenamedate
obj.removeExpiredBackupfiles = function (func) {
if (parent.config.settings.autobackup && (typeof parent.config.settings.autobackup.keeplastdaysbackup == 'number')) {
let cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - parent.config.settings.autobackup.keeplastdaysbackup);
fs.readdir(parent.backuppath, function (err, dir) {
try {
if (err == null) {
if (dir.length > 0) {
let fileName = parent.config.settings.autobackup.backupname;
let checked = 0;
let removed = 0;
for (var i in dir) {
var name = dir[i];
parent.debug('backup', "checking file: ", path.join(parent.backuppath, name));
if (name.startsWith(fileName) && name.endsWith('.zip')) {
var timex = name.substring(fileName.length, name.length - 4).split('-');
if (timex.length == 5) {
checked++;
var fileDate = new Date(parseInt(timex[0]), parseInt(timex[1]) - 1, parseInt(timex[2]), parseInt(timex[3]), parseInt(timex[4]));
if (fileDate && (cutoffDate > fileDate)) {
console.log("Removing expired backup file: ", path.join(parent.backuppath, name));
fs.unlink(path.join(parent.backuppath, name), function (err) { if (err) { console.error(err.message); if (func) {func('Error removing: ' + err.message); } } });
removed++;
}
}
else { parent.debug('backup', "file: " + name + " timestamp failure: ", timex); }
}
}
let mesg= 'Checked ' + checked + ' candidates in ' + parent.backuppath + '. Removed ' + removed + ' expired backupfiles using cutoffDate: '+ cutoffDate.toLocaleString('default', { dateStyle: 'short', timeStyle: 'short' });
parent.debug (mesg);
if (func) { func(mesg); }
} else { console.error('No files found in ' + parent.backuppath + '. There should be at least one.')}
}
else
{ console.error(err); parent.addServerWarning( 'Reading files in backup directory ' + parent.backuppath + ' failed, check errorlog: ' + err.message, true); }
} catch (ex) { console.error(ex); parent.addServerWarning( 'Something went wrong during removeExpiredBackupfiles, check errorlog: ' +ex.message, true); }
});
}
}
// Perform cloud backup
obj.performCloudBackup = function (filename, func) {
// WebDAV Backup
if ((typeof parent.config.settings.autobackup == 'object') && (typeof parent.config.settings.autobackup.webdav == 'object')) {
const xdateTimeSort = function (a, b) { if (a.xdate > b.xdate) return 1; if (a.xdate < b.xdate) return -1; return 0; }
parent.debug( 'backup', 'Entering WebDAV backup');
if (func) { func('Entering WebDAV backup.'); }
const xdateTimeSort = function (a, b) { if (a.xdate > b.xdate) return 1; if (a.xdate < b.xdate) return -1; return 0; }
// Fetch the folder name
var webdavfolderName = 'MeshCentral-Backups';
if (typeof parent.config.settings.autobackup.webdav.foldername == 'string') { webdavfolderName = parent.config.settings.autobackup.webdav.foldername; }
@ -3741,23 +3791,28 @@ module.exports.CreateDB = function (parent, func) {
// Clean up our WebDAV folder
function performWebDavCleanup(client) {
if ((typeof parent.config.settings.autobackup.webdav.maxfiles == 'number') && (parent.config.settings.autobackup.webdav.maxfiles > 1)) {
let fileName = (typeof parent.config.settings.autobackup.backupname == 'string') ? parent.config.settings.autobackup.backupname : 'meshcentral-autobackup-';
let fileName = parent.config.settings.autobackup.backupname;
//only files matching our backupfilename
let directoryItems = client.getDirectoryContents(webdavfolderName, { deep: false, glob: "/**/" + fileName + "*.zip" });
directoryItems.then(
function (files) {
for (var i in files) { files[i].xdate = new Date(files[i].lastmod); }
files.sort(xdateTimeSort);
parent.debug('backup','WebDAV filtered directory contents: ' + JSON.stringify(files, null, 4));
while (files.length >= parent.config.settings.autobackup.webdav.maxfiles) {
client.deleteFile(files.shift().filename).then(function (state) {
if (func) { func('WebDAV file deleted.'); }
let delFile = files.shift().filename;
client.deleteFile(delFile).then(function (state) {
parent.debug('backup','WebDAV file deleted: ' + delFile);
if (func) { func('WebDAV file deleted: ' + delFile); }
}).catch(function (err) {
if (func) { func('WebDAV (deleteFile) error: ' + err); }
console.error(err);
if (func) { func('WebDAV (deleteFile) error: ' + err.message); }
});
}
}
).catch(function (err) {
if (func) { func('WebDAV (getDirectoryContents) error: ' + err); }
console.error(err);
if (func) { func('WebDAV (getDirectoryContents) error: ' + err.message); }
});
}
}
@ -3766,14 +3821,14 @@ module.exports.CreateDB = function (parent, func) {
function performWebDavUpload(client, filepath) {
require('fs').stat(filepath, function(err,stat){
var fileStream = require('fs').createReadStream(filepath);
fileStream.on('close', function () { if (func) { func('WebDAV upload completed'); } })
fileStream.on('error', function (err) { if (func) { func('WebDAV (fileUpload) error: ' + err); } })
fileStream.on('close', function () { console.log('WebDAV upload completed: ' + webdavfolderName + '/' + require('path').basename(filepath)); if (func) { func('WebDAV upload completed: ' + webdavfolderName + '/' + require('path').basename(filepath)); } })
fileStream.on('error', function (err) { console.error(err); if (func) { func('WebDAV (fileUpload) error: ' + err.message); } })
fileStream.pipe(client.createWriteStream('/' + webdavfolderName + '/' + require('path').basename(filepath), { headers: { "Content-Length": stat.size } }));
if (func) { func('Uploading using WebDAV...'); }
parent.debug('backup', 'Uploading using WebDAV to: ' + parent.config.settings.autobackup.webdav.url);
if (func) { func('Uploading using WebDAV to: ' + parent.config.settings.autobackup.webdav.url); }
});
}
if (func) { func('Attempting WebDAV upload...'); }
const { createClient } = require('webdav');
const client = createClient(parent.config.settings.autobackup.webdav.url, {
username: parent.config.settings.autobackup.webdav.username,
@ -3787,19 +3842,23 @@ module.exports.CreateDB = function (parent, func) {
performWebDavUpload(client, filename);
}else{
client.createDirectory(webdavfolderName, {recursive: true}).then(function (a) {
if (func) { func('WebDAV folder created'); }
console.log('backup','WebDAV folder created: ' + webdavfolderName);
if (func) { func('WebDAV folder created: ' + webdavfolderName); }
performWebDavUpload(client, filename);
}).catch(function (err) {
if (func) { func('WebDAV (createDirectory) error: ' + err); }
console.error(err);
if (func) { func('WebDAV (createDirectory) error: ' + err.message); }
});
}
}).catch(function (err) {
if (func) { func('WebDAV (exists) error: ' + err); }
console.error(err);
if (func) { func('WebDAV (exists) error: ' + err.message); }
});
}
// Google Drive Backup
if ((typeof parent.config.settings.autobackup == 'object') && (typeof parent.config.settings.autobackup.googledrive == 'object')) {
parent.debug( 'backup', 'Entering Google Drive backup');
obj.Get('GoogleDriveBackup', function (err, docs) {
if ((err != null) || (docs.length != 1) || (docs[0].state != 3)) return;
if (func) { func('Attempting Google Drive upload...'); }
@ -3878,6 +3937,7 @@ module.exports.CreateDB = function (parent, func) {
// S3 Backup
if ((typeof parent.config.settings.autobackup == 'object') && (typeof parent.config.settings.autobackup.s3 == 'object')) {
parent.debug( 'backup', 'Entering S3 backup');
var s3folderName = 'MeshCentral-Backups';
if (typeof parent.config.settings.autobackup.s3.foldername == 'string') { s3folderName = parent.config.settings.autobackup.s3.foldername; }
// Construct the config object

View file

@ -62,8 +62,8 @@ ENV MONGO_URL=""
ENV HOSTNAME="localhost"
ENV ALLOW_NEW_ACCOUNTS="true"
ENV ALLOWPLUGINS="false"
ENV LOCALSESSIONRECORDING="false"
ENV MINIFY="true"
ENV LOCALSESSIONRECORDING="true"
ENV MINIFY="false"
ENV WEBRTC="false"
ENV IFRAME="false"
ENV SESSION_KEY=""
@ -83,8 +83,8 @@ COPY --from=builder /opt/meshcentral/meshcentral /opt/meshcentral/meshcentral
COPY ./docker/startup.sh ./startup.sh
COPY ./docker/config.json.template /opt/meshcentral/config.json.template
# install dependencies from package.json and nedb
RUN cd meshcentral && npm install && npm install nedb
# install dependencies from package.json
RUN cd meshcentral && npm install
# NOTE: ALL MODULES MUST HAVE A VERSION NUMBER AND THE VERSION MUST MATCH THAT USED IN meshcentral.js mainStart()
RUN if ! [ -z "$INCLUDE_MONGODBTOOLS" ]; then cd meshcentral && npm install mongodb@4.13.0 saslprep@1.0.3; fi

View file

@ -21,9 +21,9 @@
"": {
"_title": "MyServer",
"_title2": "Servername",
"minify": true,
"minify": false,
"NewAccounts": true,
"localSessionRecording": false,
"localSessionRecording": true,
"_userNameIsEmail": true,
"_certUrl": "my.reverse.proxy"
}

View file

@ -18,7 +18,7 @@ else
sed -i "s/\"NewAccounts\": true/\"NewAccounts\": $ALLOW_NEW_ACCOUNTS/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"enabled\": false/\"enabled\": $ALLOWPLUGINS/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"localSessionRecording\": false/\"localSessionRecording\": $LOCALSESSIONRECORDING/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"minify\": true/\"minify\": $MINIFY/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"minify\": false/\"minify\": $MINIFY/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"WebRTC\": false/\"WebRTC\": $WEBRTC/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"AllowFraming\": false/\"AllowFraming\": $IFRAME/" meshcentral-data/"${CONFIG_FILE}"
if [ -z "$SESSION_KEY" ]; then

View file

@ -123,6 +123,10 @@ Use of the optional file `plugin_name.js` in the optional folder `modules_meshco
Much of MeshCentral revolves around returning objects for your structures, and plugins are no different. Within your plugin you can traverse all the way up to the web server and MeshCentral Server classes to access all the functionality those layers provide. This is done by passing the current object to newly created objects, and assigning that reference to a `parent` variable within that object.
## Ping-Pong
If you build a plugin which makes use of `meshrelay.ashx`, keep in mind to either handle ping-pong messages (`serverPing`, `serverPong`) on the control channel or to request MeshCentral to not send such messages through sending the `noping=1` parameter in the connection URL. For a deeper sight search for "PING/PONG" in `meshrelay.js`.
## Versioning
Versioning your plugin correctly and consistently is essential to ensure users of your plugin are prompted to upgrade when it is available. Semantic versioning is recommended.

View file

@ -321,7 +321,7 @@ function setup() { InstallModules(['image-size'], start); }
function start() { startEx(process.argv); }
function startEx(argv) {
if (argv.length > 2) { indexFile(argv[2]); } else {
log("MeshCentral Session Recodings Processor");
log("MeshCentral Session Recordings Processor");
log("This tool will index a .mcrec file so that the player can seek thru the file.");
log("");
log(" Usage: node mcrec [file]");

View file

@ -1936,8 +1936,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
change = 1; // Don't save this change as an event to the db, so no log=1.
parent.removePmtFromAllOtherNodes(device); // We need to make sure to remove this push messaging token from any other device on this server, all domains included.
}
if ((command.users != null) && (Array.isArray(command.users)) && (device.users != command.users)) { device.users = command.users; change = 1; } // Don't save this to the db.
if ((command.lusers != null) && (Array.isArray(command.lusers)) && (device.lusers != command.lusers)) { device.lusers = command.lusers; change = 1; } // Don't save this to the db.
if ((mesh.mtype == 2) && (!args.wanonly)) {
// In WAN mode, the hostname of a computer is not important. Don't log hostname changes.
if (device.host != obj.remoteaddr) { device.host = obj.remoteaddr; change = 1; changes.push('host'); }

View file

@ -886,6 +886,11 @@
"default": 24,
"description": "How often should the autobackup run in hours from the second meshcentral starts up? Default is every 24 hours"
},
"backupHour": {
"type": "integer",
"default": 0,
"description": "At which hour the autobackup should run. This forces a daily backup, overrules a custom 'backupIntervalHours'."
},
"keepLastDaysBackup": {
"type": "integer",
"default": 10,
@ -1168,6 +1173,11 @@
"default": 2,
"description": "Valid numbers are 1 and 2, changes the style of the login page and some secondary pages."
},
"showModernUIToggle": {
"type": "boolean",
"default": false,
"description": "When set to true, the user will be able to toggle between the modern and classic UI."
},
"title": {
"type": "string",
"default": "MeshCentral",
@ -1962,6 +1972,11 @@
"default": false,
"description": "If true, user consent is accepted after the timeout."
},
"autoAcceptIfNoUser": {
"type": "boolean",
"default": false,
"description": "If true, user consent is accepted if no user is logged in."
},
"oldStyle": {
"type": "boolean",
"default": false,
@ -2165,6 +2180,11 @@
"default": null,
"description": "When set, idle users will be disconnected after a set amounts of minutes."
},
"logoutOnIdleSessionTimeout": {
"type": "boolean",
"default": true,
"description": "Determines whether MeshCentral should logout after the session idle timeout elapsed or should just disconnect remote desktop, terminal and files."
},
"userConsentFlags": {
"type": "object",
"description": "Use this section to require user consent for this domain.",
@ -2895,12 +2915,10 @@
},
"user": {
"type": "string",
"format": "string",
"description": "SMTP username."
},
"pass": {
"type": "string",
"format": "string",
"description": "SMTP password."
},
"tls": {
@ -3253,7 +3271,6 @@
]
}
],
"additionalProperties": false,
"properties": {
"newAccounts": {
"type": "boolean",
@ -3461,8 +3478,7 @@
"required": [
"client_id",
"client_secret"
],
"additionalProperties": false
]
},
"issuer": {
"type": [
@ -3552,8 +3568,7 @@
}
}
}
},
"additionalProperties": false
}
},
"custom": {
"type": "object",
@ -3604,8 +3619,7 @@
"type": "string",
"description": "REQUIRED IF USING GROUPS: Customer ID from Google Workspace Admin Console (https://admin.google.com/ac/accountsettings/profile)"
}
},
"additionalProperties": false
}
},
"groups": {
"type": "object",
@ -3657,8 +3671,7 @@
"default": "groups",
"description": "Custom claim to use."
}
},
"additionalProperties": false
}
}
}
}
@ -3723,8 +3736,7 @@
"description": "EAB HMAC KEY",
"default": ""
}
},
"additionalProperties": false
}
}
},
"required": [
@ -3819,12 +3831,10 @@
},
"user": {
"type": "string",
"format": "string",
"description": "SMTP username."
},
"pass": {
"type": "string",
"format": "string",
"description": "SMTP password."
},
"tls": {

View file

@ -583,8 +583,11 @@ function CreateMeshCentralServer(config, args) {
// Launch MeshCentral as a child server and monitor it.
obj.launchChildServer = function (startArgs) {
const child_process = require('child_process');
const isInspectorAttached = (()=> { try { return require('node:inspector').url() !== undefined; } catch (_) { return false; } }).call();
const logFromChildProcess = isInspectorAttached ? () => {} : console.log.bind(console);
try { if (process.traceDeprecation === true) { startArgs.unshift('--trace-deprecation'); } } catch (ex) { }
try { if (process.traceProcessWarnings === true) { startArgs.unshift('--trace-warnings'); } } catch (ex) { }
if (startArgs[0] != "--disable-proto=delete") startArgs.unshift("--disable-proto=delete")
childProcess = child_process.execFile(process.argv[0], startArgs, { maxBuffer: Infinity, cwd: obj.parentpath }, function (error, stdout, stderr) {
if (childProcess.xrestart == 1) {
setTimeout(function () { obj.launchChildServer(startArgs); }, 500); // This is an expected restart.
@ -656,12 +659,12 @@ function CreateMeshCentralServer(config, args) {
else if (data.indexOf('Starting self upgrade to: ') >= 0) { obj.args.specificupdate = data.substring(26).split('\r')[0].split('\n')[0]; childProcess.xrestart = 3; }
var datastr = data;
while (datastr.endsWith('\r') || datastr.endsWith('\n')) { datastr = datastr.substring(0, datastr.length - 1); }
console.log(datastr);
logFromChildProcess(datastr);
});
childProcess.stderr.on('data', function (data) {
var datastr = data;
while (datastr.endsWith('\r') || datastr.endsWith('\n')) { datastr = datastr.substring(0, datastr.length - 1); }
console.log('ERR: ' + datastr);
logFromChildProcess('ERR: ' + datastr);
if (data.startsWith('le.challenges[tls-sni-01].loopback')) { return; } // Ignore this error output from GreenLock
if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); }
obj.logError(data);
@ -1348,7 +1351,7 @@ function CreateMeshCentralServer(config, args) {
}
// Check if the database is capable of performing a backup
obj.db.checkBackupCapability(function (err, msg) { if (msg != null) { obj.addServerWarning(msg, true) } });
// Moved behind autobackup config init in startex4: obj.db.checkBackupCapability(function (err, msg) { if (msg != null) { obj.addServerWarning(msg, true) } });
// Load configuration for database if needed
if (obj.args.loadconfigfromdb) {
@ -1656,7 +1659,7 @@ function CreateMeshCentralServer(config, args) {
}
// Setup agent error log
if ((obj.config) && (obj.config.settings) && (obj.config.settings.agentlogdump != null)) {
if ((obj.config) && (obj.config.settings) && (obj.config.settings.agentlogdump)) {
obj.fs.open(obj.path.join(obj.datapath, 'agenterrorlogs.txt'), 'a', function (err, fd) { obj.agentErrorLog = fd; })
}
@ -2016,6 +2019,7 @@ function CreateMeshCentralServer(config, args) {
// Start periodic maintenance
obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
//obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 10 * 1); // DEBUG: Run this more often
// Dispatch an event that the server is now running
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' });
@ -2105,18 +2109,19 @@ function CreateMeshCentralServer(config, args) {
if (obj.config.settings.autobackup == null || obj.config.settings.autobackup === true) { obj.config.settings.autobackup = {backupintervalhours: 24, keeplastdaysbackup: 10}; };
if (typeof obj.config.settings.autobackup.backupintervalhours != 'number') { obj.config.settings.autobackup.backupintervalhours = 24; };
if (typeof obj.config.settings.autobackup.keeplastdaysbackup != 'number') { obj.config.settings.autobackup.keeplastdaysbackup = 10; };
if (obj.config.settings.autobackup.backuphour != null ) { obj.config.settings.autobackup.backupintervalhours = 24; if ((typeof obj.config.settings.autobackup.backuphour != 'number') || (obj.config.settings.autobackup.backuphour > 23 || obj.config.settings.autobackup.backuphour < 0 )) { obj.config.settings.autobackup.backuphour = 0; }}
else {obj.config.settings.autobackup.backuphour = -1 };
//arrayfi in case of string and remove possible ', ' space. !! If a string instead of an array is passed, it will be split by ',' so *{.txt,.log} won't work in that case !!
if (!obj.config.settings.autobackup.backupignorefilesglob) {obj.config.settings.autobackup.backupignorefilesglob = []}
else if (typeof obj.config.settings.autobackup.backupignorefilesglob == 'string') { obj.config.settings.autobackup.backupignorefilesglob = obj.config.settings.autobackup.backupignorefilesglob.replaceAll(', ', ',').split(','); };
if (!obj.config.settings.autobackup.backupskipfoldersglob) {obj.config.settings.autobackup.backupskipfoldersglob = []}
else if (typeof obj.config.settings.autobackup.backupskipfoldersglob == 'string') { obj.config.settings.autobackup.backupskipfoldersglob = obj.config.settings.autobackup.backupskipfoldersglob.replaceAll(', ', ',').split(','); };
if (typeof obj.config.settings.autobackup.backuppath == 'string') { obj.backuppath = (obj.config.settings.autobackup.backuppath = (obj.path.resolve(obj.config.settings.autobackup.backuppath))) } else { obj.config.settings.autobackup.backuppath = obj.backuppath };
if (typeof obj.config.settings.autobackup.backupname != 'string') { obj.config.settings.autobackup.backupname = 'meshcentral-autobackup-'};
}
// Check that autobackup path is not within the "meshcentral-data" folder.
if ((typeof obj.config.settings.autobackup == 'object') && (typeof obj.config.settings.autobackup.backuppath == 'string') && (obj.path.normalize(obj.config.settings.autobackup.backuppath).startsWith(obj.path.normalize(obj.datapath)))) {
addServerWarning("Backup path can't be set within meshcentral-data folder, backup settings ignored.", 21);
obj.config.settings.autobackup = {backupintervalhours: -1}; //block console autobackup
}
// Check if the database is capable of performing a backup
obj.db.checkBackupCapability(function (err, msg) { if (msg != null) { obj.addServerWarning(msg, true) } });
// Load Intel AMT passwords from the "amtactivation.log" file
obj.loadAmtActivationLogPasswords(function (amtPasswords) {
@ -2278,14 +2283,19 @@ function CreateMeshCentralServer(config, args) {
// Check if we need to perform an automatic backup
function checkAutobackup() {
if (obj.config.settings.autobackup.backupintervalhours >= 1) {
if (obj.config.settings.autobackup.backupintervalhours >= 1 ) {
obj.db.Get('LastAutoBackupTime', function (err, docs) {
if (err != null) return;
if (err != null) { console.error("checkAutobackup: Error getting LastBackupTime from DB"); return}
var lastBackup = 0;
const now = new Date().getTime();
const currentdate = new Date();
let currentHour = currentdate.getHours();
let now = currentdate.getTime();
if (docs.length == 1) { lastBackup = docs[0].value; }
const delta = now - lastBackup;
if (delta > (obj.config.settings.autobackup.backupintervalhours * 60 * 60 * 1000)) {
//const delta = 9999999999; // DEBUG: backup always
obj.debug ('backup', 'Entering checkAutobackup, lastAutoBackupTime: ' + new Date(lastBackup).toLocaleString('default', { dateStyle: 'medium', timeStyle: 'short' }) + ', delta: ' + (delta/(1000*60*60)).toFixed(2) + ' hours');
//start autobackup if interval has passed or at configured hour, whichever comes first. When an hour schedule is missed, it will make a backup immediately.
if ((delta > (obj.config.settings.autobackup.backupintervalhours * 60 * 60 * 1000)) || ((currentHour == obj.config.settings.autobackup.backuphour) && (delta >= 2 * 60 * 60 * 1000))) {
// A new auto-backup is required.
obj.db.Set({ _id: 'LastAutoBackupTime', value: now }); // Save the current time in the database
obj.db.performBackup(); // Perform the backup
@ -3936,6 +3946,7 @@ function CreateMeshCentralServer(config, args) {
function logWarnEvent(msg) { if (obj.servicelog != null) { obj.servicelog.warn(msg); } console.log(msg); }
function logErrorEvent(msg) { if (obj.servicelog != null) { obj.servicelog.error(msg); } console.error(msg); }
obj.getServerWarnings = function () { return serverWarnings; }
// TODO: migrate from other addServerWarning function and add timestamp
obj.addServerWarning = function (msg, id, args, print) { serverWarnings.push({ msg: msg, id: id, args: args }); if (print !== false) { console.log("WARNING: " + msg); } }
// auth.log functions
@ -4106,6 +4117,7 @@ function InstallModuleEx(modulenames, args, func) {
process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop(); meshserver = null; } console.log('Server Ctrl-C exit...'); process.exit(); });
// Add a server warning, warnings will be shown to the administrator on the web application
// TODO: migrate to obj.addServerWarning?
const serverWarnings = [];
function addServerWarning(msg, id, args, print) { serverWarnings.push({ msg: msg, id: id, args: args }); if (print !== false) { console.log("WARNING: " + msg); } }
@ -4212,7 +4224,7 @@ function mainStart() {
if (mstsc == false) { config.domains[i].mstsc = false; }
if (config.domains[i].ssh == true) { ssh = true; }
if ((typeof config.domains[i].authstrategies == 'object')) {
if (passport.length == 0) { passport = ['passport','connect-flash']; } // Passport v0.6.0 requires a patch, see https://github.com/jaredhanson/passport/issues/904 and include connect-flash here to display errors
if (passport.indexOf('passport') == -1) { passport.push('passport','connect-flash'); } // Passport v0.6.0 requires a patch, see https://github.com/jaredhanson/passport/issues/904 and include connect-flash here to display errors
if ((typeof config.domains[i].authstrategies.twitter == 'object') && (typeof config.domains[i].authstrategies.twitter.clientid == 'string') && (typeof config.domains[i].authstrategies.twitter.clientsecret == 'string') && (passport.indexOf('passport-twitter') == -1)) { passport.push('passport-twitter'); }
if ((typeof config.domains[i].authstrategies.google == 'object') && (typeof config.domains[i].authstrategies.google.clientid == 'string') && (typeof config.domains[i].authstrategies.google.clientsecret == 'string') && (passport.indexOf('passport-google-oauth20') == -1)) { passport.push('passport-google-oauth20'); }
if ((typeof config.domains[i].authstrategies.github == 'object') && (typeof config.domains[i].authstrategies.github.clientid == 'string') && (typeof config.domains[i].authstrategies.github.clientsecret == 'string') && (passport.indexOf('passport-github2') == -1)) { passport.push('passport-github2'); }

View file

@ -2243,6 +2243,7 @@ function serverConnect() {
case 'removeDeviceShare':
case 'userbroadcast': { // BROADCAST
if ((settings.cmd == 'shell') || (settings.cmd == 'upload') || (settings.cmd == 'download')) return;
if ((data.type == 'runcommands') && (settings.cmd != 'runcommand')) return;
if ((settings.multiresponse != null) && (settings.multiresponse > 1)) { settings.multiresponse--; break; }
if (data.responseid == 'meshctrl') {
if (data.meshid) { console.log(data.result, data.meshid); }
@ -2665,8 +2666,8 @@ function getDevicesThatMatchFilter(nodes, x) {
} else if (tagSearch != null) {
// Tag filter
for (var d in nodes) {
if ((nodes[d].tags == null) && (tagSearch == '')) { r.push(d); }
else if (nodes[d].tags != null) { for (var j in nodes[d].tags) { if (nodes[d].tags[j].toLowerCase() == tagSearch) { r.push(d); break; } } }
if ((nodes[d].tags == null) && (tagSearch == '')) { r.push(nodes[d]); }
else if (nodes[d].tags != null) { for (var j in nodes[d].tags) { if (nodes[d].tags[j].toLowerCase() == tagSearch) { r.push(nodes[d]); break; } } }
}
} else if (agentTagSearch != null) {
// Agent Tag filter

View file

@ -847,7 +847,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, id, func) {
return;
}
// Write the recording file header
parent.parent.debug('relay', 'Relay: Started recoding to file: ' + recFullFilename);
parent.parent.debug('relay', 'Relay: Started recording to file: ' + recFullFilename);
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 () {
@ -1347,6 +1347,7 @@ function CreateMeshRelayEx2(parent, ws, req, domain, user, cookie) {
if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
}
if (typeof domain.notificationmessages == 'object') {

View file

@ -445,15 +445,15 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
relayinfo.peer1.sendPeerImage();
} else {
// Write the recording file header
parent.parent.debug('relay', 'Relay: Started recoding to file: ' + recFullFilename);
parent.parent.debug('relay', 'Relay: Started recording to file: ' + recFullFilename);
var metadata = {
magic: 'MeshCentralRelaySession',
ver: 1,
userid: sessionUser._id,
username: sessionUser.name,
sessionid: obj.id,
ipaddr1: (obj.req == null) ? null : obj.req.clientIp,
ipaddr2: ((obj.peer == null) || (obj.peer.req == null)) ? null : obj.peer.req.clientIp,
ipaddr1: ((obj.peer == null) || (obj.peer.req == null)) ? null : obj.peer.req.clientIp,
ipaddr2: (obj.req == null) ? null : obj.req.clientIp,
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)
@ -896,6 +896,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
}
if (typeof domain.notificationmessages == 'object') {
@ -934,6 +935,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
}
if (typeof domain.notificationmessages == 'object') {
@ -952,6 +954,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
}
if (typeof domain.notificationmessages == 'object') {
@ -1004,6 +1007,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
}
if (typeof domain.notificationmessages == 'object') {

View file

@ -600,7 +600,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
}
if (typeof domain.userconsentflags == 'number') { serverinfo.consent = domain.userconsentflags; }
if ((typeof domain.usersessionidletimeout == 'number') && (domain.usersessionidletimeout > 0)) { serverinfo.timeout = (domain.usersessionidletimeout * 60 * 1000); }
if ((typeof domain.usersessionidletimeout == 'number') && (domain.usersessionidletimeout > 0)) {serverinfo.timeout = (domain.usersessionidletimeout * 60 * 1000); }
if (typeof domain.logoutonidlesessiontimeout == 'boolean') {
serverinfo.logoutonidlesessiontimeout = domain.logoutonidlesessiontimeout;
} else {
// Default
serverinfo.logoutonidlesessiontimeout = true;
}
if (user.siteadmin === SITERIGHT_ADMIN) {
if (parent.parent.config.settings.managealldevicegroups.indexOf(user._id) >= 0) { serverinfo.manageAllDeviceGroups = true; }
if (obj.crossDomain === true) { serverinfo.crossDomain = []; for (var i in parent.parent.config.domains) { serverinfo.crossDomain.push(i); } }
@ -922,7 +928,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Get a short file and send it back on the web socket
if (common.validateString(command.file, 1, 4096) == false) return;
const scpath = meshPathToRealPath(command.path, user); // This will also check access rights
if (scpath == null) break;
if ((scpath == null) || (command.file !== parent.path.basename(command.file))) break;
const filePath = parent.path.join(scpath, command.file);
fs.stat(filePath, function (err, stat) {
if ((err != null) || (stat == null) || (stat.size >= 204800)) return;
@ -937,7 +943,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateString(command.file, 1, 4096) == false) return;
if (typeof command.data != 'string') return;
const scpath = meshPathToRealPath(command.path, user); // This will also check access rights
if (scpath == null) break;
if ((scpath == null) || (command.file !== parent.path.basename(command.file))) break;
const filePath = parent.path.join(scpath, command.file);
var data = null;
try { data = Buffer.from(command.data, 'base64'); } catch (ex) { return; }
@ -997,6 +1003,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
}
if (typeof domain.notificationmessages == 'object') {
@ -3072,7 +3079,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
if (commandsOk == true) {
var theCommand = { action: 'runcommands', type: command.type, cmds: command.cmds, runAsUser: command.runAsUser, reply: command.reply, responseid: command.responseid };
if (parent.parent.multiServer != null) { // peering setup
var agent = parent.wsagents[node._id];
if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
// Send the commands to the agent
try { agent.send(JSON.stringify(theCommand)); } catch (ex) { }
if (command.responseid != null && command.reply == false) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
// Send out an event that these commands where run on this device
var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'runcommands', msg: 'Running commands', msgid: msgid, cmds: command.cmds, cmdType: command.type, runAsUser: command.runAsUser, domain: domain.id };
parent.parent.DispatchEvent(targets, obj, event);
} else if (parent.parent.multiServer != null) { // peering setup
// Send the commands to the agent
parent.parent.multiServer.DispatchMessage({ action: 'agentCommand', nodeid: node._id, command: theCommand});
if (command.responseid != null && command.reply == false) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
@ -3080,20 +3096,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'runcommands', msg: 'Running commands', msgid: msgid, cmds: command.cmds, cmdType: command.type, runAsUser: command.runAsUser, domain: domain.id };
parent.parent.multiServer.DispatchEvent(targets, obj, event);
} else { // normal setup
// Get the agent and run the commands
var agent = parent.wsagents[node._id];
if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
// Send the commands to the agent
try { agent.send(JSON.stringify(theCommand)); } catch (ex) { }
if (command.responseid != null && command.reply == false) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
// Send out an event that these commands where run on this device
var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'runcommands', msg: 'Running commands', msgid: msgid, cmds: command.cmds, cmdType: command.type, runAsUser: command.runAsUser, domain: domain.id };
parent.parent.DispatchEvent(targets, obj, event);
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
}
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
}
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid command type' })); } catch (ex) { } }
@ -5072,285 +5076,295 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'getDeviceDetails': {
if ((common.validateStrArray(command.nodeids, 1) == false) && (command.nodeids != null)) break; // Check nodeids
if (common.validateString(command.type, 3, 4) == false) break; // Check type
const links = parent.GetAllMeshIdWithRights(user);
const extraids = getUserExtraIds();
db.GetAllTypeNoTypeFieldMeshFiltered(links, extraids, domain.id, 'node', null, obj.deviceSkip, obj.deviceLimit, function (err, docs) {
if (docs == null) return;
const ids = [];
if (command.nodeids != null) {
// Create a list of node ids and query them for last device connection time
for (var i in command.nodeids) { ids.push('lc' + command.nodeids[i]); }
} else {
// Create a list of node ids for this user and query them for last device connection time
for (var i in docs) { ids.push('lc' + docs[i]._id); }
}
db.GetAllIdsOfType(ids, domain.id, 'lastconnect', function (err, docs) {
const lastConnects = {};
if (docs != null) { for (var i in docs) { lastConnects[docs[i]._id] = docs[i]; } }
// Create a list of node ids and query them for last device connection time
const ids = []
for (var i in command.nodeids) { ids.push('lc' + command.nodeids[i]); }
db.GetAllIdsOfType(ids, domain.id, 'lastconnect', function (err, docs) {
const lastConnects = {};
if (docs != null) { for (var i in docs) { lastConnects[docs[i]._id] = docs[i]; } }
getDeviceDetailedInfo(command.nodeids, command.type, function (results, type) {
for (var i = 0; i < results.length; i++) {
// Remove any device system and network information is we do not have details rights to this device
if ((parent.GetNodeRights(user, results[i].node.meshid, results[i].node._id) & MESHRIGHT_DEVICEDETAILS) == 0) {
delete results[i].sys; delete results[i].net;
}
// Merge any last connection information
const lc = lastConnects['lc' + results[i].node._id];
if (lc != null) { delete lc._id; delete lc.type; delete lc.meshid; delete lc.domain; results[i].lastConnect = lc; }
// Remove any connectivity and power state information, that should not be in the database anyway.
// TODO: Find why these are sometimes saved in the db.
if (results[i].node.conn != null) { delete results[i].node.conn; }
if (results[i].node.pwr != null) { delete results[i].node.pwr; }
if (results[i].node.agct != null) { delete results[i].node.agct; }
if (results[i].node.cict != null) { delete results[i].node.cict; }
// Add the connection state
var state = parent.parent.GetConnectivityState(results[i].node._id);
if (state) {
results[i].node.conn = state.connectivity;
results[i].node.pwr = state.powerState;
if ((state.connectivity & 1) != 0) { var agent = parent.wsagents[results[i].node._id]; if (agent != null) { results[i].node.agct = agent.connectTime; } }
// Use the connection time of the CIRA/Relay connection
if ((state.connectivity & 2) != 0) {
var ciraConnection = parent.parent.mpsserver.GetConnectionToNode(results[i].node._id, null, true);
if ((ciraConnection != null) && (ciraConnection.tag != null)) { results[i].node.cict = ciraConnection.tag.connectTime; }
getDeviceDetailedInfo(command.nodeids, command.type, function (results, type) {
for (var i = 0; i < results.length; i++) {
// Remove any device system and network information is we do not have details rights to this device
if ((parent.GetNodeRights(user, results[i].node.meshid, results[i].node._id) & MESHRIGHT_DEVICEDETAILS) == 0) {
delete results[i].sys; delete results[i].net;
}
}
}
var output = null;
if (type == 'csv') {
try {
// Create the CSV file
output = 'id,name,rname,host,icon,ip,osdesc,groupname,av,update,firewall,bitlocker,avdetails,tags,cpu,osbuild,biosDate,biosVendor,biosVersion,biosSerial,biosMode,boardName,boardVendor,boardVersion,productUuid,tpmversion,tpmmanufacturer,tpmmanufacturerversion,tpmisactivated,tpmisenabled,tpmisowned,totalMemory,agentOpenSSL,agentCommitDate,agentCommitHash,agentCompileTime,netIfCount,macs,addresses,lastConnectTime,lastConnectAddr\r\n';
for (var i = 0; i < results.length; i++) {
const nodeinfo = results[i];
// Merge any last connection information
const lc = lastConnects['lc' + results[i].node._id];
if (lc != null) { delete lc._id; delete lc.type; delete lc.meshid; delete lc.domain; results[i].lastConnect = lc; }
// Node information
if (nodeinfo.node != null) {
const n = nodeinfo.node;
output += csvClean(n._id) + ',' + csvClean(n.name) + ',' + csvClean(n.rname ? n.rname : '') + ',' + csvClean(n.host ? n.host : '') + ',' + (n.icon ? n.icon : 1) + ',' + (n.ip ? n.ip : '') + ',' + (n.osdesc ? csvClean(n.osdesc) : '') + ',' + csvClean(parent.meshes[n.meshid].name);
if (typeof n.wsc == 'object') {
output += ',' + csvClean(n.wsc.antiVirus ? n.wsc.antiVirus : '') + ',' + csvClean(n.wsc.autoUpdate ? n.wsc.autoUpdate : '') + ',' + csvClean(n.wsc.firewall ? n.wsc.firewall : '')
} else { output += ',,,'; }
if (typeof n.volumes == 'object') {
var bitlockerdetails = '', firstbitlocker = true;
for (var a in n.volumes) { if (typeof n.volumes[a].protectionStatus !== 'undefined') { if (firstbitlocker) { firstbitlocker = false; } else { bitlockerdetails += '|'; } bitlockerdetails += a + '/' + n.volumes[a].volumeStatus; } }
output += ',' + csvClean(bitlockerdetails);
} else {
output += ',';
}
if (typeof n.av == 'object') {
var avdetails = '', firstav = true;
for (var a in n.av) { if (typeof n.av[a].product == 'string') { if (firstav) { firstav = false; } else { avdetails += '|'; } avdetails += (n.av[a].product + '/' + ((n.av[a].enabled) ? 'enabled' : 'disabled') + '/' + ((n.av[a].updated) ? 'updated' : 'notupdated')); } }
output += ',' + csvClean(avdetails);
} else {
output += ',';
}
if (typeof n.tags == 'object') {
var tagsdetails = '', firsttags = true;
for (var a in n.tags) { if (firsttags) { firsttags = false; } else { tagsdetails += '|'; } tagsdetails += n.tags[a]; }
output += ',' + csvClean(tagsdetails);
} else {
output += ',';
}
} else {
output += ',,,,,,,,,,,,,,,,,,,';
// Remove any connectivity and power state information, that should not be in the database anyway.
// TODO: Find why these are sometimes saved in the db.
if (results[i].node.conn != null) { delete results[i].node.conn; }
if (results[i].node.pwr != null) { delete results[i].node.pwr; }
if (results[i].node.agct != null) { delete results[i].node.agct; }
if (results[i].node.cict != null) { delete results[i].node.cict; }
// Add the connection state
var state = parent.parent.GetConnectivityState(results[i].node._id);
if (state) {
results[i].node.conn = state.connectivity;
results[i].node.pwr = state.powerState;
if ((state.connectivity & 1) != 0) { var agent = parent.wsagents[results[i].node._id]; if (agent != null) { results[i].node.agct = agent.connectTime; } }
// Use the connection time of the CIRA/Relay connection
if ((state.connectivity & 2) != 0) {
var ciraConnection = parent.parent.mpsserver.GetConnectionToNode(results[i].node._id, null, true);
if ((ciraConnection != null) && (ciraConnection.tag != null)) { results[i].node.cict = ciraConnection.tag.connectTime; }
}
}
}
// System infomation
if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.windows)) {
// Windows
output += ',';
if (nodeinfo.sys.hardware.windows.cpu && (nodeinfo.sys.hardware.windows.cpu.length > 0) && (typeof nodeinfo.sys.hardware.windows.cpu[0].Name == 'string')) { output += csvClean(nodeinfo.sys.hardware.windows.cpu[0].Name); }
output += ',';
if (nodeinfo.sys.hardware.windows.osinfo && (nodeinfo.sys.hardware.windows.osinfo.BuildNumber)) { output += csvClean(nodeinfo.sys.hardware.windows.osinfo.BuildNumber); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_date)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_date); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_vendor)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_vendor); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_version)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_version); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_serial)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_serial); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_mode)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_mode); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.board_name)) { output += csvClean(nodeinfo.sys.hardware.identifiers.board_name); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.board_vendor)) { output += csvClean(nodeinfo.sys.hardware.identifiers.board_vendor); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.board_version)) { output += csvClean(nodeinfo.sys.hardware.identifiers.board_version); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.product_uuid)) { output += csvClean(nodeinfo.sys.hardware.identifiers.product_uuid); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.SpecVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.SpecVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerId) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerId); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsActivated) { output += csvClean(nodeinfo.sys.hardware.tpm.IsActivated ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsEnabled) { output += csvClean(nodeinfo.sys.hardware.tpm.IsEnabled ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsOwned) { output += csvClean(nodeinfo.sys.hardware.tpm.IsOwned ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.windows.memory) {
var totalMemory = 0;
for (var j in nodeinfo.sys.hardware.windows.memory) {
if (nodeinfo.sys.hardware.windows.memory[j].Capacity) {
if (typeof nodeinfo.sys.hardware.windows.memory[j].Capacity == 'number') { totalMemory += nodeinfo.sys.hardware.windows.memory[j].Capacity; }
if (typeof nodeinfo.sys.hardware.windows.memory[j].Capacity == 'string') { totalMemory += parseInt(nodeinfo.sys.hardware.windows.memory[j].Capacity); }
}
var output = null;
if (type == 'csv') {
try {
// Create the CSV file
output = 'id,name,rname,host,icon,ip,osdesc,groupname,av,update,firewall,bitlocker,avdetails,tags,lastbootuptime,cpu,osbuild,biosDate,biosVendor,biosVersion,biosSerial,biosMode,boardName,boardVendor,boardVersion,productUuid,tpmversion,tpmmanufacturer,tpmmanufacturerversion,tpmisactivated,tpmisenabled,tpmisowned,totalMemory,agentOpenSSL,agentCommitDate,agentCommitHash,agentCompileTime,netIfCount,macs,addresses,lastConnectTime,lastConnectAddr\r\n';
for (var i = 0; i < results.length; i++) {
const nodeinfo = results[i];
// Node information
if (nodeinfo.node != null) {
const n = nodeinfo.node;
output += csvClean(n._id) + ',' + csvClean(n.name) + ',' + csvClean(n.rname ? n.rname : '') + ',' + csvClean(n.host ? n.host : '') + ',' + (n.icon ? n.icon : 1) + ',' + (n.ip ? n.ip : '') + ',' + (n.osdesc ? csvClean(n.osdesc) : '') + ',' + csvClean(parent.meshes[n.meshid].name);
if (typeof n.wsc == 'object') {
output += ',' + csvClean(n.wsc.antiVirus ? n.wsc.antiVirus : '') + ',' + csvClean(n.wsc.autoUpdate ? n.wsc.autoUpdate : '') + ',' + csvClean(n.wsc.firewall ? n.wsc.firewall : '')
} else { output += ',,,'; }
if (typeof n.volumes == 'object') {
var bitlockerdetails = '', firstbitlocker = true;
for (var a in n.volumes) { if (typeof n.volumes[a].protectionStatus !== 'undefined') { if (firstbitlocker) { firstbitlocker = false; } else { bitlockerdetails += '|'; } bitlockerdetails += a + '/' + n.volumes[a].volumeStatus; } }
output += ',' + csvClean(bitlockerdetails);
} else {
output += ',';
}
output += csvClean('' + totalMemory);
if (typeof n.av == 'object') {
var avdetails = '', firstav = true;
for (var a in n.av) { if (typeof n.av[a].product == 'string') { if (firstav) { firstav = false; } else { avdetails += '|'; } avdetails += (n.av[a].product + '/' + ((n.av[a].enabled) ? 'enabled' : 'disabled') + '/' + ((n.av[a].updated) ? 'updated' : 'notupdated')); } }
output += ',' + csvClean(avdetails);
} else {
output += ',';
}
if (typeof n.tags == 'object') {
var tagsdetails = '', firsttags = true;
for (var a in n.tags) { if (firsttags) { firsttags = false; } else { tagsdetails += '|'; } tagsdetails += n.tags[a]; }
output += ',' + csvClean(tagsdetails);
} else {
output += ',';
}
if (typeof n.lastbootuptime == 'number') { output += ',' + n.lastbootuptime; } else { output += ','; }
} else {
output += ',,,,,,,,,,,,,,,,,,,,';
}
} else if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.mobile)) {
// Mobile
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.bootloader)) { output += csvClean(nodeinfo.sys.hardware.mobile.bootloader); }
output += ',';
output += ',';
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.model)) { output += csvClean(nodeinfo.sys.hardware.mobile.model); }
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.brand)) { output += csvClean(nodeinfo.sys.hardware.mobile.brand); }
output += ',';
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.id)) { output += csvClean(nodeinfo.sys.hardware.mobile.id); }
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
} else if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.linux)) {
// Linux
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.cpu_name)) { output += csvClean(nodeinfo.sys.hardware.identifiers.cpu_name); }
output += ',,';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.bios_date)) { output += csvClean(nodeinfo.sys.hardware.linux.bios_date); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.bios_vendor)) { output += csvClean(nodeinfo.sys.hardware.linux.bios_vendor); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.bios_version)) { output += csvClean(nodeinfo.sys.hardware.linux.bios_version); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.product_serial)) { output += csvClean(nodeinfo.sys.hardware.linux.product_serial); }
else if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_serial)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_serial); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_mode)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_mode); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.board_name)) { output += csvClean(nodeinfo.sys.hardware.linux.board_name); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.board_vendor)) { output += csvClean(nodeinfo.sys.hardware.linux.board_vendor); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.board_version)) { output += csvClean(nodeinfo.sys.hardware.linux.board_version); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.product_uuid)) { output += csvClean(nodeinfo.sys.hardware.linux.product_uuid); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.SpecVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.SpecVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerId) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerId); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsActivated) { output += csvClean(nodeinfo.sys.hardware.tpm.IsActivated ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsEnabled) { output += csvClean(nodeinfo.sys.hardware.tpm.IsEnabled ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsOwned) { output += csvClean(nodeinfo.sys.hardware.tpm.IsOwned ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.linux.memory) {
if (nodeinfo.sys.hardware.linux.memory.Memory_Device) {
// System infomation
if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.windows)) {
// Windows
output += ',';
if (nodeinfo.sys.hardware.windows.cpu && (nodeinfo.sys.hardware.windows.cpu.length > 0) && (typeof nodeinfo.sys.hardware.windows.cpu[0].Name == 'string')) { output += csvClean(nodeinfo.sys.hardware.windows.cpu[0].Name); }
output += ',';
if (nodeinfo.sys.hardware.windows.osinfo && (nodeinfo.sys.hardware.windows.osinfo.BuildNumber)) { output += csvClean(nodeinfo.sys.hardware.windows.osinfo.BuildNumber); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_date)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_date); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_vendor)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_vendor); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_version)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_version); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_serial)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_serial); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_mode)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_mode); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.board_name)) { output += csvClean(nodeinfo.sys.hardware.identifiers.board_name); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.board_vendor)) { output += csvClean(nodeinfo.sys.hardware.identifiers.board_vendor); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.board_version)) { output += csvClean(nodeinfo.sys.hardware.identifiers.board_version); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.product_uuid)) { output += csvClean(nodeinfo.sys.hardware.identifiers.product_uuid); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.SpecVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.SpecVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerId) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerId); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsActivated) { output += csvClean(nodeinfo.sys.hardware.tpm.IsActivated ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsEnabled) { output += csvClean(nodeinfo.sys.hardware.tpm.IsEnabled ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsOwned) { output += csvClean(nodeinfo.sys.hardware.tpm.IsOwned ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.windows.memory) {
var totalMemory = 0;
for (var j in nodeinfo.sys.hardware.linux.memory.Memory_Device) {
if (nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size) {
if (typeof nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size == 'number') { totalMemory += nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size; }
if (typeof nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size == 'string') { totalMemory += parseInt(nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size); }
for (var j in nodeinfo.sys.hardware.windows.memory) {
if (nodeinfo.sys.hardware.windows.memory[j].Capacity) {
if (typeof nodeinfo.sys.hardware.windows.memory[j].Capacity == 'number') { totalMemory += nodeinfo.sys.hardware.windows.memory[j].Capacity; }
if (typeof nodeinfo.sys.hardware.windows.memory[j].Capacity == 'string') { totalMemory += parseInt(nodeinfo.sys.hardware.windows.memory[j].Capacity); }
}
}
output += csvClean('' + (totalMemory * Math.pow(1024, 3)));
output += csvClean('' + totalMemory);
}
}
} else {
output += ',,,,,,,,,,,,,,,,,,';
}
// Agent information
if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.agentvers)) {
output += ',';
if (nodeinfo.sys.hardware.agentvers.openssl) { output += csvClean(nodeinfo.sys.hardware.agentvers.openssl); }
output += ',';
if (nodeinfo.sys.hardware.agentvers.commitDate) { output += csvClean(nodeinfo.sys.hardware.agentvers.commitDate); }
output += ',';
if (nodeinfo.sys.hardware.agentvers.commitHash) { output += csvClean(nodeinfo.sys.hardware.agentvers.commitHash); }
output += ',';
if (nodeinfo.sys.hardware.agentvers.compileTime) { output += csvClean(nodeinfo.sys.hardware.agentvers.compileTime); }
} else {
output += ',,,,';
}
// Network interfaces
if ((nodeinfo.net) && (nodeinfo.net.netif2)) {
output += ',';
output += Object.keys(nodeinfo.net.netif2).length; // Interface count
var macs = [], addresses = [];
for (var j in nodeinfo.net.netif2) {
if (Array.isArray(nodeinfo.net.netif2[j])) {
for (var k = 0; k < nodeinfo.net.netif2[j].length; k++) {
if (typeof nodeinfo.net.netif2[j][k].mac == 'string') { macs.push(nodeinfo.net.netif2[j][k].mac); }
if (typeof nodeinfo.net.netif2[j][k].address == 'string') { addresses.push(nodeinfo.net.netif2[j][k].address); }
} else if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.mobile)) {
// Mobile
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.bootloader)) { output += csvClean(nodeinfo.sys.hardware.mobile.bootloader); }
output += ',';
output += ',';
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.model)) { output += csvClean(nodeinfo.sys.hardware.mobile.model); }
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.brand)) { output += csvClean(nodeinfo.sys.hardware.mobile.brand); }
output += ',';
output += ',';
if (nodeinfo.sys.hardware.mobile && (nodeinfo.sys.hardware.mobile.id)) { output += csvClean(nodeinfo.sys.hardware.mobile.id); }
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
output += ',';
} else if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.linux)) {
// Linux
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.cpu_name)) { output += csvClean(nodeinfo.sys.hardware.identifiers.cpu_name); }
output += ',,';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.bios_date)) { output += csvClean(nodeinfo.sys.hardware.linux.bios_date); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.bios_vendor)) { output += csvClean(nodeinfo.sys.hardware.linux.bios_vendor); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.bios_version)) { output += csvClean(nodeinfo.sys.hardware.linux.bios_version); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.product_serial)) { output += csvClean(nodeinfo.sys.hardware.linux.product_serial); }
else if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_serial)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_serial); }
output += ',';
if (nodeinfo.sys.hardware.identifiers && (nodeinfo.sys.hardware.identifiers.bios_mode)) { output += csvClean(nodeinfo.sys.hardware.identifiers.bios_mode); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.board_name)) { output += csvClean(nodeinfo.sys.hardware.linux.board_name); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.board_vendor)) { output += csvClean(nodeinfo.sys.hardware.linux.board_vendor); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.board_version)) { output += csvClean(nodeinfo.sys.hardware.linux.board_version); }
output += ',';
if (nodeinfo.sys.hardware.linux && (nodeinfo.sys.hardware.linux.product_uuid)) { output += csvClean(nodeinfo.sys.hardware.linux.product_uuid); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.SpecVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.SpecVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerId) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerId); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.ManufacturerVersion) { output += csvClean(nodeinfo.sys.hardware.tpm.ManufacturerVersion); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsActivated) { output += csvClean(nodeinfo.sys.hardware.tpm.IsActivated ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsEnabled) { output += csvClean(nodeinfo.sys.hardware.tpm.IsEnabled ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.tpm && nodeinfo.sys.hardware.tpm.IsOwned) { output += csvClean(nodeinfo.sys.hardware.tpm.IsOwned ? 'true' : 'false'); }
output += ',';
if (nodeinfo.sys.hardware.linux.memory) {
if (nodeinfo.sys.hardware.linux.memory.Memory_Device) {
var totalMemory = 0;
for (var j in nodeinfo.sys.hardware.linux.memory.Memory_Device) {
if (nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size) {
if (typeof nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size == 'number') { totalMemory += nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size; }
if (typeof nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size == 'string') { totalMemory += parseInt(nodeinfo.sys.hardware.linux.memory.Memory_Device[j].Size); }
}
}
output += csvClean('' + (totalMemory * Math.pow(1024, 3)));
}
}
} else {
output += ',,,,,,,,,,,,,,,,,,';
}
output += ',';
output += csvClean(macs.join(' ')); // MACS
output += ',';
output += csvClean(addresses.join(' ')); // Addresses
} else {
output += ',,,';
}
// Last connection information
if (nodeinfo.lastConnect) {
output += ',';
if (nodeinfo.lastConnect.time) {
// Last connection time
if ((typeof command.l == 'string') && (typeof command.tz == 'string')) {
output += csvClean(new Date(nodeinfo.lastConnect.time).toLocaleString(command.l, { timeZone: command.tz }))
} else {
output += nodeinfo.lastConnect.time;
// Agent information
if ((nodeinfo.sys) && (nodeinfo.sys.hardware) && (nodeinfo.sys.hardware.agentvers)) {
output += ',';
if (nodeinfo.sys.hardware.agentvers.openssl) { output += csvClean(nodeinfo.sys.hardware.agentvers.openssl); }
output += ',';
if (nodeinfo.sys.hardware.agentvers.commitDate) { output += csvClean(nodeinfo.sys.hardware.agentvers.commitDate); }
output += ',';
if (nodeinfo.sys.hardware.agentvers.commitHash) { output += csvClean(nodeinfo.sys.hardware.agentvers.commitHash); }
output += ',';
if (nodeinfo.sys.hardware.agentvers.compileTime) { output += csvClean(nodeinfo.sys.hardware.agentvers.compileTime); }
} else {
output += ',,,,';
}
// Network interfaces
if ((nodeinfo.net) && (nodeinfo.net.netif2)) {
output += ',';
output += Object.keys(nodeinfo.net.netif2).length; // Interface count
var macs = [], addresses = [];
for (var j in nodeinfo.net.netif2) {
if (Array.isArray(nodeinfo.net.netif2[j])) {
for (var k = 0; k < nodeinfo.net.netif2[j].length; k++) {
if (typeof nodeinfo.net.netif2[j][k].mac == 'string') { macs.push(nodeinfo.net.netif2[j][k].mac); }
if (typeof nodeinfo.net.netif2[j][k].address == 'string') { addresses.push(nodeinfo.net.netif2[j][k].address); }
}
}
}
output += ',';
output += csvClean(macs.join(' ')); // MACS
output += ',';
output += csvClean(addresses.join(' ')); // Addresses
} else {
output += ',,,';
}
output += ',';
if (typeof nodeinfo.lastConnect.addr == 'string') { output += csvClean(nodeinfo.lastConnect.addr); } // Last connection address and port
} else {
output += ',,';
// Last connection information
if (nodeinfo.lastConnect) {
output += ',';
if (nodeinfo.lastConnect.time) {
// Last connection time
if ((typeof command.l == 'string') && (typeof command.tz == 'string')) {
output += csvClean(new Date(nodeinfo.lastConnect.time).toLocaleString(command.l, { timeZone: command.tz }))
} else {
output += nodeinfo.lastConnect.time;
}
}
output += ',';
if (typeof nodeinfo.lastConnect.addr == 'string') { output += csvClean(nodeinfo.lastConnect.addr); } // Last connection address and port
} else {
output += ',,';
}
output += '\r\n';
}
} catch (ex) { console.log(ex); }
} else {
// Create the JSON file
output += '\r\n';
// Add the device group name to each device
for (var i = 0; i < results.length; i++) {
const nodeinfo = results[i];
if (nodeinfo.node) {
const mesh = parent.meshes[nodeinfo.node.meshid];
if (mesh) { results[i].node.groupname = mesh.name; }
}
}
} catch (ex) { console.log(ex); }
} else {
// Create the JSON file
// Add the device group name to each device
for (var i = 0; i < results.length; i++) {
const nodeinfo = results[i];
if (nodeinfo.node) {
const mesh = parent.meshes[nodeinfo.node.meshid];
if (mesh) { results[i].node.groupname = mesh.name; }
}
output = JSON.stringify(results, null, 2);
}
output = JSON.stringify(results, null, 2);
}
try { ws.send(JSON.stringify({ action: 'getDeviceDetails', data: output, type: type })); } catch (ex) { }
try { ws.send(JSON.stringify({ action: 'getDeviceDetails', data: output, type: type })); } catch (ex) { }
});
});
});
break;
}
case 'endDesktopMultiplex': {
@ -5599,7 +5613,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
'heapdump': [serverUserCommandHeapDump, ""],
'heapdump2': [serverUserCommandHeapDump2, ""],
'help': [serverUserCommandHelp, ""],
'info': [serverUserCommandInfo, "Returns the most immidiatly useful information about this server, including MeshCentral and NodeJS versions. This is often information required to file a bug."],
'info': [serverUserCommandInfo, "Returns the most immidiatly useful information about this server, including MeshCentral and NodeJS versions. This is often information required to file a bug. Optionally use info h for human readable form."],
'le': [serverUserCommandLe, ""],
'lecheck': [serverUserCommandLeCheck, ""],
'leevents': [serverUserCommandLeEvents, ""],
@ -7545,7 +7559,26 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
function serverUserCommandInfo(cmdData) {
var info = {};
function convertSeconds (s, form) {
if (!['long', 'shortprecise'].includes(form)) {
form = 'shortprecise';
}
let t = {}, r = '';
t.d = Math.floor(s / (24 * 3600));
s %= 24 * 3600;
t.h= Math.floor(s / 3600);
s %= 3600;
t.m = Math.floor(s / 60);
t.s =(s%60).toFixed(0);
if ( form == 'long') {
r = t.d + ((t.d == 1) ? ' day, ' : ' days, ') + t.h + ((t.h == 1) ? ' hour, ' : ' hours, ') + t.m + ((t.m == 1) ? ' minute, ' : ' minutes, ') + t.s+ ((t.s == 1) ? ' second' : ' seconds');
} else if (form == 'shortprecise') {
r = String(t.d).padStart(2, '0') + ':' + String(t.h).padStart(2, '0') + ':' + String(t.m).padStart(2, '0') + ':' + String((s%60).toFixed(2)).padStart(5, '0') + 's';
}
return r;
}
var info = {}, arg = null, t = {}, r = '';
if ((cmdData.cmdargs['_'] != null) && (cmdData.cmdargs['_'][0] != null)) { arg = cmdData.cmdargs['_'][0].toLowerCase(); }
try { info.meshVersion = 'v' + parent.parent.currentVer; } catch (ex) { }
try { info.nodeVersion = process.version; } catch (ex) { }
try { info.runMode = (["Hybrid (LAN + WAN) mode", "WAN mode", "LAN mode"][(args.lanonly ? 2 : (args.wanonly ? 1 : 0))]); } catch (ex) { }
@ -7557,9 +7590,24 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try { info.platform = process.platform; } catch (ex) { }
try { info.arch = process.arch; } catch (ex) { }
try { info.pid = process.pid; } catch (ex) { }
try { info.uptime = process.uptime(); } catch (ex) { }
try { info.cpuUsage = process.cpuUsage(); } catch (ex) { }
try { info.memoryUsage = process.memoryUsage(); } catch (ex) { }
if (arg == 'h') {
try {
info.uptime = convertSeconds(process.uptime(), 'long');
info.cpuUsage = {
system: (convertSeconds(process.cpuUsage().system /1000000)),
user: (convertSeconds(process.cpuUsage().user /1000000))
}
info.memoryUsage = {};
for (const [key,value] of Object.entries(process.memoryUsage())){
info.memoryUsage[key] = ([value]/1048576).toFixed(2) + 'Mb';
}
} catch (ex) { }
}
else {
try { info.uptime = process.uptime(); } catch (ex) { }
try { info.cpuUsage = process.cpuUsage(); } catch (ex) { }
try { info.memoryUsage = process.memoryUsage(); } catch (ex) { }
}
try { info.warnings = parent.parent.getServerWarnings(); } catch (ex) { console.log(ex); }
try { info.allDevGroupManagers = parent.parent.config.settings.managealldevicegroups; } catch (ex) { }
try { if (process.traceDeprecation == true) { info.traceDeprecation = true; } } catch (ex) { }

View file

@ -32,7 +32,7 @@ module.exports.CreateMonitoring = function (parent, args) {
blockedUsers: { description: "Blocked Users" }, // blockedUsers
blockedAgents: { description: "Blocked Agents" }, // blockedAgents
};
obj.guageMetrics = { // Guage Metrics always start at 0 and can increase and decrease
obj.gaugeMetrics = { // Gauge Metrics always start at 0 and can increase and decrease
ConnectedIntelAMT: { description: "Connected Intel AMT" }, // parent.mpsserver.ciraConnections[i].length
UserAccounts: { description: "User Accounts" }, // Object.keys(parent.webserver.users).length
DeviceGroups: { description: "Device Groups" }, // parent.webserver.meshes (ONLY WHERE deleted=null)
@ -42,6 +42,7 @@ module.exports.CreateMonitoring = function (parent, args) {
RelaySessions: { description: "Relay Sessions" }, // parent.webserver.relaySessionCount
RelayCount: { description: "Relay Count" } // Object.keys(parent.webserver.wsrelays).length30bb4fb74dfb758d36be52a7
}
obj.collectors = [];
if (parent.config.settings.prometheus != null) { // Create Prometheus Monitoring Endpoint
if ((typeof parent.config.settings.prometheus == 'number') && ((parent.config.settings.prometheus < 1) || (parent.config.settings.prometheus > 65535))) {
console.log('Promethus port number is invalid, Prometheus metrics endpoint has be disabled');
@ -51,8 +52,8 @@ module.exports.CreateMonitoring = function (parent, args) {
obj.prometheus = require('prom-client');
const collectDefaultMetrics = obj.prometheus.collectDefaultMetrics;
collectDefaultMetrics();
for (const key in obj.guageMetrics) {
obj.guageMetrics[key].prometheus = new obj.prometheus.Gauge({ name: 'meshcentral_' + String(key).toLowerCase(), help: obj.guageMetrics[key].description });
for (const key in obj.gaugeMetrics) {
obj.gaugeMetrics[key].prometheus = new obj.prometheus.Gauge({ name: 'meshcentral_' + String(key).toLowerCase(), help: obj.gaugeMetrics[key].description });
}
for (const key in obj.counterMetrics) {
obj.counterMetrics[key].prometheus = new obj.prometheus.Counter({ name: 'meshcentral_' + String(key).toLowerCase(), help: obj.counterMetrics[key].description });
@ -67,7 +68,7 @@ module.exports.CreateMonitoring = function (parent, args) {
// Count the number of device groups that are not deleted
var activeDeviceGroups = 0;
for (var i in parent.webserver.meshes) { if (parent.webserver.meshes[i].deleted == null) { activeDeviceGroups++; } } // This is not ideal for performance, we want to dome something better.
var guages = {
var gauges = {
UserAccounts: Object.keys(parent.webserver.users).length,
DeviceGroups: activeDeviceGroups,
AgentSessions: Object.keys(parent.webserver.wsagents).length,
@ -79,10 +80,10 @@ module.exports.CreateMonitoring = function (parent, args) {
};
if (parent.mpsserver != null) {
for (var i in parent.mpsserver.ciraConnections) {
guages.ConnectedIntelAMT += parent.mpsserver.ciraConnections[i].length;
gauges.ConnectedIntelAMT += parent.mpsserver.ciraConnections[i].length;
}
}
for (const key in guages) { obj.guageMetrics[key].prometheus.set(guages[key]); }
for (const key in gauges) { obj.gaugeMetrics[key].prometheus.set(gauges[key]); }
// Take a look at agent errors
var agentstats = parent.webserver.getAgentStats();
const counters = {
@ -103,6 +104,7 @@ module.exports.CreateMonitoring = function (parent, args) {
};
for (const key in counters) { obj.counterMetrics[key].prometheus.reset(); obj.counterMetrics[key].prometheus.inc(counters[key]); }
res.set('Content-Type', obj.prometheus.register.contentType);
await Promise.all(obj.collectors.map((collector) => (collector(req, res))));
res.end(await obj.prometheus.register.metrics());
} catch (ex) {
console.log(ex);
@ -111,4 +113,5 @@ module.exports.CreateMonitoring = function (parent, args) {
});
}
}
return obj;
}

View file

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "1.1.36",
"version": "1.1.42",
"keywords": [
"Remote Device Management",
"Remote Device Monitoring",

View file

@ -139,7 +139,7 @@ module.exports.pluginHandler = function (parent) {
try {
obj.plugins[p][hookName](...args);
} catch (e) {
console.log("Error occurred while running plugin hook" + p + ':' + hookName + ' (' + e + ')');
console.log("Error occurred while running plugin hook " + p + ':' + hookName, e);
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -155,7 +155,6 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
if (Msg[1] == 1) { obj.ProcessCopyRectMsg(Msg[2]); }
else if (Msg[1] == 2) { obj.Canvas.drawImage(Msg[2], obj.rotX(Msg[3], Msg[4]), obj.rotY(Msg[3], Msg[4])); delete Msg[2]; }
obj.PendingOperations.splice(i, 1);
delete Msg;
obj.TilesDrawn++;
if ((obj.TilesDrawn == obj.tilesReceived) && (obj.KillDraw < obj.TilesDrawn)) { obj.KillDraw = obj.TilesDrawn = obj.tilesReceived = 0; }
return true;
@ -221,12 +220,16 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
if ((cmd == 3) || (cmd == 4) || (cmd == 7)) { X = (view[4] << 8) + view[5]; Y = (view[6] << 8) + view[7]; }
if (obj.debugmode > 2) { console.log('CMD', cmd, cmdsize, X, Y); }
// Fix for view being too large for String.fromCharCode.apply()
var chunkSize = 10000;
let result = '';
for (let i = 0; i < view.length; i += chunkSize) { result += String.fromCharCode.apply(null, view.slice(i, i + chunkSize)); }
// Record the command if needed
if (obj.recordedData != null) {
if (cmdsize > 65000) {
obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(27) + obj.shortToStr(8) + obj.intToStr(cmdsize) + obj.shortToStr(cmd) + obj.shortToStr(0) + obj.shortToStr(0) + obj.shortToStr(0) + String.fromCharCode.apply(null, view)));
obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(27) + obj.shortToStr(8) + obj.intToStr(cmdsize) + obj.shortToStr(cmd) + obj.shortToStr(0) + obj.shortToStr(0) + obj.shortToStr(0) + result));
} else {
obj.recordedData.push(recordingEntry(2, 1, String.fromCharCode.apply(null, view)));
obj.recordedData.push(recordingEntry(2, 1, result));
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,8 +4,8 @@ document.addEventListener("DOMContentLoaded", function () {
// Load saved theme from local storage
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
const safeTheme = encodeURIComponent(savedTheme);
themeStylesheet.href = `styles/themes/${safeTheme}/bootstrap.min.css`;
const safeTheme = ((savedTheme != 'default') ? encodeURIComponent(savedTheme) : encodeURIComponent('..'));
themeStylesheet.href = `styles/themes/${safeTheme}/bootstrap-min.css`;
}
// Initialize Select2 on all select elements with the 'select2' class

View file

@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,function(){return r=[function(e,t,r){function n(){}Object.defineProperty(t,"__esModule",{value:!0}),n.prototype.activate=function(e){this._terminal=e},n.prototype.dispose=function(){},n.prototype.fit=function(){var e,t=this.proposeDimensions();t&&this._terminal&&(e=this._terminal._core,this._terminal.rows===t.rows&&this._terminal.cols===t.cols||(e._renderService.clear(),this._terminal.resize(t.cols,t.rows)))},n.prototype.proposeDimensions=function(){var e,t,r,n;if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement)return e=this._terminal._core,n=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(n.getPropertyValue("height")),n=Math.max(0,parseInt(n.getPropertyValue("width"))),t=window.getComputedStyle(this._terminal.element),r=r-(parseInt(t.getPropertyValue("padding-top"))+parseInt(t.getPropertyValue("padding-bottom"))),n=n-(parseInt(t.getPropertyValue("padding-right"))+parseInt(t.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth,{cols:Math.max(2,Math.floor(n/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(r/e._renderService.dimensions.actualCellHeight))}},t.FitAddon=n}],n={},o.m=r,o.c=n,o.d=function(e,t,r){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(o.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)o.d(r,n,function(e){return t[e]}.bind(null,n));return r},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=0);function o(e){var t;return(n[e]||(t=n[e]={i:e,l:!1,exports:{}},r[e].call(t.exports,t,t.exports,o),t.l=!0,t)).exports}var r,n})
((e,t)=>{"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()})(window,function(){return r=[function(e,t,r){function n(){}Object.defineProperty(t,"__esModule",{value:!0}),n.prototype.activate=function(e){this._terminal=e},n.prototype.dispose=function(){},n.prototype.fit=function(){var e,t=this.proposeDimensions();t&&this._terminal&&(e=this._terminal._core,this._terminal.rows===t.rows&&this._terminal.cols===t.cols||(e._renderService.clear(),this._terminal.resize(t.cols,t.rows)))},n.prototype.proposeDimensions=function(){var e,t,r,n;if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement)return e=this._terminal._core,n=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(n.getPropertyValue("height")),n=Math.max(0,parseInt(n.getPropertyValue("width"))),t=window.getComputedStyle(this._terminal.element),r=r-(parseInt(t.getPropertyValue("padding-top"))+parseInt(t.getPropertyValue("padding-bottom"))),n=n-(parseInt(t.getPropertyValue("padding-right"))+parseInt(t.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth,{cols:Math.max(2,Math.floor(n/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(r/e._renderService.dimensions.actualCellHeight))}},t.FitAddon=n}],n={},o.m=r,o.c=n,o.d=function(e,t,r){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(o.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)o.d(r,n,function(e){return t[e]}.bind(null,n));return r},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=0);function o(e){var t;return(n[e]||(t=n[e]={i:e,l:!1,exports:{}},r[e].call(t.exports,t,t.exports,o),t.l=!0,t)).exports}var r,n})

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
"undefined"==typeof ZLIB&&alert("ZLIB is not defined. SRC zlib.js before zlib-adler32.js"),function(){var b=65521,v=5552;ZLIB.adler32=function(r,e,o,t){if("string"==typeof e){var a,d=r,c=e,C=o,h=t,A=d>>>16&65535;if(d&=65535,1==h)d+=255&c.charCodeAt(C),b<=d&&(d-=b),b<=(A+=d)&&(A-=b);else{if(null===c)return 1;if(h<16){for(;h--;)A+=d+=255&c.charCodeAt(C++);return b<=d&&(d-=b),d|(A%=b)<<16}for(;v<=h;){for(h-=v,a=347;A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A+=d+=255&c.charCodeAt(C++))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)),--a;);d%=b,A%=b}if(h){for(;16<=h;)h-=16,A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A+=d+=255&c.charCodeAt(C++))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++));for(;h--;)A+=d+=255&c.charCodeAt(C++);d%=b,A%=b}}return d|A<<16}var f,n=r,i=e,u=o,l=t,s=n>>>16&65535;if(n&=65535,1==l)n+=i[u],b<=n&&(n-=b),b<=(s+=n)&&(s-=b);else{if(null===i)return 1;if(l<16){for(;l--;)s+=n+=i[u++];return b<=n&&(n-=b),n|(s%=b)<<16}for(;v<=l;){for(l-=v,f=347;s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s+=n+=i[u++])+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]),--f;);n%=b,s%=b}if(l){for(;16<=l;)l-=16,s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s+=n+=i[u++])+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]))+(n+=i[u++]);for(;l--;)s+=n+=i[u++];n%=b,s%=b}}return n|s<<16},ZLIB.adler32_combine=function(r,e,o){var t,a;return o<0?4294967295:(a=(o%=b)*(t=65535&r),b<=(t+=(65535&e)+b-1)&&(t-=b),b<=t&&(t-=b),b<<1<=(a=a%b+((r>>16&65535)+(e>>16&65535)+b-o))&&(a-=b<<1),b<=a&&(a-=b),t|a<<16)}}()
"undefined"==typeof ZLIB&&alert("ZLIB is not defined. SRC zlib.js before zlib-adler32.js"),(()=>{var b=65521,v=5552;ZLIB.adler32=function(r,e,o,t){if("string"==typeof e){var a,d=r,c=e,C=o,h=t,A=d>>>16&65535;if(d&=65535,1==h)d+=255&c.charCodeAt(C),b<=d&&(d-=b),b<=(A+=d)&&(A-=b);else{if(null===c)return 1;if(h<16){for(;h--;)A+=d+=255&c.charCodeAt(C++);return b<=d&&(d-=b),d|(A%=b)<<16}for(;v<=h;){for(h-=v,a=347;A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A+=d+=255&c.charCodeAt(C++))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)),--a;);d%=b,A%=b}if(h){for(;16<=h;)h-=16,A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A=(A+=d+=255&c.charCodeAt(C++))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++)))+(d+=255&c.charCodeAt(C++));for(;h--;)A+=d+=255&c.charCodeAt(C++);d%=b,A%=b}}return d|A<<16}var f,n=r,i=e,l=o,u=t,s=n>>>16&65535;if(n&=65535,1==u)n+=i[l],b<=n&&(n-=b),b<=(s+=n)&&(s-=b);else{if(null===i)return 1;if(u<16){for(;u--;)s+=n+=i[l++];return b<=n&&(n-=b),n|(s%=b)<<16}for(;v<=u;){for(u-=v,f=347;s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s+=n+=i[l++])+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]),--f;);n%=b,s%=b}if(u){for(;16<=u;)u-=16,s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s+=n+=i[l++])+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]))+(n+=i[l++]);for(;u--;)s+=n+=i[l++];n%=b,s%=b}}return n|s<<16},ZLIB.adler32_combine=function(r,e,o){var t,a;return o<0?4294967295:(a=(o%=b)*(t=65535&r),b<=(t+=(65535&e)+b-1)&&(t-=b),b<=t&&(t-=b),b<<1<=(a=a%b+((r>>16&65535)+(e>>16&65535)+b-o))&&(a-=b<<1),b<=a&&(a-=b),t|a<<16)}})()

View file

@ -1 +1 @@
"undefined"==typeof ZLIB&&alert("ZLIB is not defined. SRC zlib.js before zlib-crc32.js"),function(){var C=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];ZLIB.crc32=function(r,e,o,n){if("string"==typeof e){var t=r,f=e,c=o,a=n;if(null==f)return 0;for(t^=4294967295;8<=a;)t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,a-=8;if(a)for(;t=C[255&(t^f.charCodeAt(c++))]^t>>>8,--a;);return 4294967295^t}var i=r,u=e,d=o,A=n;if(null==u)return 0;for(i^=4294967295;8<=A;)i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,i=C[255&(i^u[d++])]^i>>>8,A-=8;if(A)for(;i=C[255&(i^u[d++])]^i>>>8,--A;);return 4294967295^i};function a(r,e){for(var o=0,n=0;e;)1&e&&(n^=r[o]),e>>=1,o++;return n}function i(r,e){for(var o=0;o<32;o++)r[o]=a(e,e[o])}ZLIB.crc32_combine=function(r,e,o){var n,t,f,c;if(!(o<=0)){for(f=new Array(32),(c=new Array(32))[0]=3988292384,n=t=1;n<32;n++)c[n]=t,t<<=1;for(i(f,c),i(c,f);i(f,c),1&o&&(r=a(f,r)),0!=(o>>=1)&&(i(c,f),1&o&&(r=a(c,r)),0!=(o>>=1)););r^=e}return r}}()
"undefined"==typeof ZLIB&&alert("ZLIB is not defined. SRC zlib.js before zlib-crc32.js"),(()=>{var C=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];function a(r,e){for(var o=0,n=0;e;)1&e&&(n^=r[o]),e>>=1,o++;return n}function i(r,e){for(var o=0;o<32;o++)r[o]=a(e,e[o])}ZLIB.crc32=function(r,e,o,n){if("string"==typeof e){var t=r,f=e,c=o,a=n;if(null==f)return 0;for(t^=4294967295;8<=a;)t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,t=C[255&(t^f.charCodeAt(c++))]^t>>>8,a-=8;if(a)for(;t=C[255&(t^f.charCodeAt(c++))]^t>>>8,--a;);return 4294967295^t}var i=r,d=e,u=o,A=n;if(null==d)return 0;for(i^=4294967295;8<=A;)i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,i=C[255&(i^d[u++])]^i>>>8,A-=8;if(A)for(;i=C[255&(i^d[u++])]^i>>>8,--A;);return 4294967295^i},ZLIB.crc32_combine=function(r,e,o){var n,t,f,c;if(!(o<=0)){for(f=new Array(32),(c=new Array(32))[0]=3988292384,n=t=1;n<32;n++)c[n]=t,t<<=1;for(i(f,c),i(c,f);i(f,c),1&o&&(r=a(f,r)),0!=(o>>=1)&&(i(c,f),1&o&&(r=a(c,r)),0!=(o>>=1)););r^=e}return r}})()

File diff suppressed because one or more lines are too long

View file

@ -247,7 +247,6 @@ body {
}
.nonenglish .topbar_td {
width: 10px;
height: 24px;
cursor: pointer;
padding-left:16px;
@ -267,6 +266,16 @@ body {
right: 3px;
}
.textnewui {
color: white;
font-weight: bold;
padding-top: 5px;
cursor: pointer;
position: absolute;
right: 0;
margin-right: 10px;
}
.LogoffLinkColor {
color:white;
}
@ -694,6 +703,10 @@ body {
text-align: center;
}
.DeviceCheckbox {
margin-top: 2px !important;
}
.deviceBarCheckbox {
width:22px;
float:left;
@ -814,15 +827,15 @@ NoMeshesPanel img {
.deviceNotifySmallDot {
position:absolute;
right:10px;
top:0px;
top:4px;
height:10px;
}
.deviceNotifySmallDotSub {
text-align:center;
color:#FFF;
height:10px;
width:10px;
height:14px;
width:14px;
padding:2px;
background-color:#00F;
border-radius:10px;
@ -2070,7 +2083,11 @@ nav .lbbuttonsel2 {
#d2devNotes,
#d2devEvent,
#d2runcmd,
#d2devMessage {
#d2devMessage,
#d2smsText,
#d2emailSubject,
#d2emailText,
#broadcastMessage {
background-color: #FFF9D3;
}
@ -2080,7 +2097,11 @@ nav .lbbuttonsel2 {
.night #d2devNotes,
.night #d2devEvent,
.night #d2runcmd,
.night #d2devMessage {
.night #d2devMessage,
.night #d2smsText,
.night #d2emailSubject,
.night #d2emailText,
.night #broadcastMessage {
background-color: black;
}
@ -2870,7 +2891,7 @@ nav .lbbuttonsel2 {
width: 28px;
}
.viewSelector3 {
.viewSelector3, .uiSelector7 {
margin-left: 2px;
margin-top: 2px;
background: url(../images/views.png) -56px 0px;
@ -2970,6 +2991,13 @@ nav .lbbuttonsel2 {
background-color: #AAA;
}
.uiSelector_end {
width: 32px;
height: 32px;
float: left;
margin: 3px;
}
.uiSelectorSel {
background-color: #BBB;
opacity: 0.8;

View file

@ -263,6 +263,16 @@ body {
right: 3px;
}
.textnewui {
color: white;
font-weight: bold;
padding-top: 5px;
cursor: pointer;
position: absolute;
right: 0;
margin-right: 10px;
}
.LogoffLinkColor {
color:white;
}
@ -2896,7 +2906,7 @@ a {
width: 28px;
}
.viewSelector3 {
.viewSelector3, .uiSelector7 {
margin-left: 2px;
margin-top: 2px;
background: url(../images/views.png) -56px 0px;
@ -2996,6 +3006,13 @@ a {
background-color: #AAA;
}
.uiSelector_end {
width: 32px;
height: 32px;
float: left;
margin: 3px;
}
.uiSelectorSel {
background-color: #BBB;
opacity: 0.8;

View file

@ -186,6 +186,7 @@
"domains": {
"": {
"_siteStyle": 2,
"_showModernUIToggle": true,
"title": "MyServer",
"title2": "Servername",
"_titlePicture": "title-sample.png",
@ -342,6 +343,7 @@
"files": "{0} requesting remote files access. Grant access?",
"consentTimeout": 30,
"autoAcceptOnTimeout": false,
"autoAcceptIfNoUser": false,
"oldStyle": true
},
"_notificationMessages": {
@ -384,6 +386,8 @@
"_agentBlockedIP": "127.0.0.1,::1",
"___userSessionIdleTimeout__": "Number of user idle minutes before auto-disconnect",
"_userSessionIdleTimeout": 30,
"___logoutOnIdleSessionTimeout": "Determines whether MeshCentral should logout after the session idle timeout elapsed or should just disconnect remote desktop, terminal and files.",
"_logoutOnIdleSessionTimeout": false,
"userConsentFlags": {
"desktopnotify": true,
"terminalnotify": true,

View file

@ -39,6 +39,7 @@ var meshCentralSourceFiles = [
"../views/messenger.handlebars",
"../views/player.handlebars",
"../views/sharing.handlebars",
"../views/sharing-mobile.handlebars",
"../views/mstsc.handlebars",
"../views/ssh.handlebars",
"../emails/account-check.html",
@ -64,6 +65,7 @@ var minifyMeshCentralSourceFiles = [
"../views/agentinvite.handlebars",
"../views/invite.handlebars",
"../views/default.handlebars",
"../views/default3.handlebars",
"../views/default-mobile.handlebars",
"../views/download.handlebars",
"../views/download2.handlebars",
@ -452,7 +454,8 @@ function startEx(argv) {
removeScriptTypeAttributes: true,
removeTagWhitespace: true,
preserveLineBreaks: false,
useShortDoctype: true
useShortDoctype: true,
log: function(a) { if (typeof a !== 'string') { console.log(a); } } // Log errors from UglifyJS to console output
});
} catch (ex) {
console.log(ex);
@ -783,10 +786,12 @@ function getStringsHtml(name, node) {
// Check if the "value" attribute exists and needs to be translated
var subnodeignore = false;
var subnodevalueignore = false;
if ((subnode.attributes != null) && (subnode.attributes.length > 0)) {
var subnodevalue = null, subnodeplaceholder = null, subnodetitle = null;
for (var j in subnode.attributes) {
if ((subnode.attributes[j].name == 'notrans') && (subnode.attributes[j].value == '1')) { subnodeignore = true; }
if ((subnode.attributes[j].name == 'notransval') && (subnode.attributes[j].value == '1')) { subnodevalueignore = true; }
if ((subnode.attributes[j].name == 'type') && (subnode.attributes[j].value == 'hidden')) { subnodeignore = true; }
if (subnode.attributes[j].name == 'value') { subnodevalue = subnode.attributes[j].value; }
if (subnode.attributes[j].name == 'placeholder') { subnodeplaceholder = subnode.attributes[j].value; }
@ -795,7 +800,7 @@ function getStringsHtml(name, node) {
if ((subnodevalue != null) && isNumber(subnodevalue) == true) { subnodevalue = null; }
if ((subnodeplaceholder != null) && isNumber(subnodeplaceholder) == true) { subnodeplaceholder = null; }
if ((subnodetitle != null) && isNumber(subnodetitle) == true) { subnodetitle = null; }
if ((subnodeignore == false) && (subnodevalue != null)) {
if ((subnodeignore == false) && (subnodevalueignore == false) && (subnodevalue != null)) {
// Add a new string to the list (value)
if (sourceStrings[subnodevalue] == null) { sourceStrings[subnodevalue] = { en: subnodevalue, xloc: [name] }; } else { if (sourceStrings[subnodevalue].xloc == null) { sourceStrings[subnodevalue].xloc = []; } sourceStrings[subnodevalue].xloc.push(name); }
}

File diff suppressed because it is too large Load diff

View file

@ -1304,6 +1304,7 @@
delete urlargs.viewmode;
delete urlargs.gotonode;
delete urlargs.gotodevicename;
delete urlargs.gotodeviceip;
delete urlargs.gotomesh;
delete urlargs.panel;
@ -1324,7 +1325,7 @@
var logoutControls = JSON.parse(decodeURIComponent('{{{logoutControls}}}'));
var authCookieRenewTimer = null;
var webRelayPort = parseInt('{{{webRelayPort}}}');
var hidePowerTimeline = {{{hidePowerTimeline}}};
var hidePowerTimeline = '{{{hidePowerTimeline}}}';
var webRelayDns = '{{{webRelayDns}}}';
var meshserver = null;
var xdr = null;
@ -1521,7 +1522,26 @@
}
function setSessionActivity() { sessionActivity = Date.now(); }
function checkIdleSessionTimeout() { var delta = (Date.now() - sessionActivity); if (delta > serverinfo.timeout) { window.location.href = 'logout'; } }
function checkIdleSessionTimeout() {
var delta = (Date.now() - sessionActivity);
if (delta > serverinfo.timeout) {
if (desktop != null) { // Disconnect remote desktop
desktop.Stop();
desktopNode = desktop = null;
}
if (terminal != null) { // Disconnect terminal
terminal.Stop();
terminal = null;
}
if (files != null) { // Disconnect files
files.Stop();
files = null;
}
if (serverinfo.logoutonidlesessiontimeout) {
window.location.href = 'logout';
}
}
}
function onMessage(server, message) {
switch (message.action) {
@ -1743,7 +1763,7 @@
var secret = message.secret;
if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
QH('d2optinfo', format("Install <a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\" rel=\"noreferrer noopener\" target=_blank>Google Authenticator</a> or a compatible application, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank> this link</a> or enter the secret below. Then, enter the current 6 digit token to activate 2-Step login.", message.url) + '<br /><br /><div style=width:100%;text-align:center><tt id=d2optsecret secret="' + message.secret + '" style=font-size:15px>' + secret + '</tt><br /><br />Token: <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress=\"return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)\" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></div>');
QH('d2optinfo', format("Install" + ' <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" rel="noreferrer noopener" target=_blank>' + "Google Authenticator" + '</a> ' + "or a compatible application, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank> this link</a> or enter the secret below. Then, enter the current 6 digit token to activate 2-Step login.", message.url) + '<br /><br /><div style=width:100%;text-align:center><tt id=d2optsecret secret="' + message.secret + '" style=font-size:15px>' + secret + '</tt><br /><br />Token: <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress=\"return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)\" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></div>');
QV('idx_dlgOkButton', true);
QE('idx_dlgOkButton', false);
Q('d2otpauthinput').focus();
@ -1753,12 +1773,12 @@
}
case 'otpauth-setup': {
if (xxdialogMode) return;
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>2-step login activation successful</b>. You will now need a valid token to login again." : "<b style=color:red>2-step login activation failed</b>. Clear the secret from the application and try again. You only have a few minutes to enter the proper code.");
setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b style=color:green>' + "Authenticator app activation successful." + '</b> ' + "You will now need a valid token to login again.") : ('<b style=color:red>' + "2-step login activation failed." + '</b> ' + "Clear the secret from the application and try again. You only have a few minutes to enter the proper code."));
break;
}
case 'otpauth-clear': {
if (xxdialogMode) return;
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>2-step login activation removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again.");
setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b>' + "Authenticator application removed." + '</b> ' + "You can reactivate this feature at any time.") : ('<b style=color:red>' + "2-step login activation removal failed." + '</b> ' + "Try again."));
break;
}
case 'otpauth-getpasswords': {
@ -2025,6 +2045,8 @@
// Change the node
node.name = message.event.node.name;
node.rname = message.event.node.rname;
node.lusers = message.event.node.lusers;
node.users = message.event.node.users;
node.host = message.event.node.host;
node.desc = message.event.node.desc;
node.publicip = message.event.node.publicip;
@ -2229,6 +2251,10 @@
var foundNode = null;
if (nodes != null) { for (var i in nodes) { if (nodes[i].name == args.gotodevicename) { foundNode = nodes[i]._id; } } }
if (foundNode) { gotoDevice(foundNode, xviewmode); go(xviewmode); }
} else if (args.gotodeviceip != null) {
var foundNode = null;
if (nodes != null) { for (var i in nodes) { if (nodes[i].ip == args.gotodeviceip) { foundNode = nodes[i]._id; } } }
if (foundNode) { gotoDevice(foundNode, xviewmode); go(xviewmode); }
} else if (args.gotomesh != null) {
if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet
gotoMesh('mesh/' + domain + '/' + args.gotomesh);
@ -4255,7 +4281,7 @@
// Draw device power bars. The bars are 766px wide.
function drawDeviceTimeline() {
if (currentNode.mtype == 3 || hidePowerTimeline) { QH('p10html2', '<br />'); return; }
if (currentNode.mtype == 3 || hidePowerTimeline === 'true') { QH('p10html2', '<br />'); return; }
var timeline = null, now = Date.now();
if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
@ -6262,7 +6288,12 @@
}
// Active Users
if (node.users && (node.users.length > 0)) { x += addDetailItem(((node.users.length > 1)?"Active Users":"Active User"), EscapeHtml(node.users.join(', '))); }
if (node.users && node.users.length > 0) {
var u = node.users.map(function(user) {
return addKeyLinkConditional(EscapeHtml(user), "Locked", (node.lusers && node.lusers.indexOf(user) >= 0));
}).join(', ');
x += addDetailItem((node.users.length > 1 ? "Active Users" : "Active User"), u);
}
if (x != '') { sections.push({ name: "Operating System", html: x, img: 'software' }); }
@ -6351,7 +6382,7 @@
}
if (hardware.network && hardware.network.dns) {
x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
x += addDetailItem("<b>DNS Servers</b>", hardware.network.dns.join(", "));
x += addDetailItem('<b>' + "DNS Servers" + '</b>', hardware.network.dns.join(", "));
x += '</div></td></tr>';
}
x += '</table>';
@ -6788,7 +6819,19 @@
if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } // Upload default core
else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } // Clear the core
else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file
else { setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", '<select id=d3coreMode style=width:230px><option value=1>' + "Upload default server core" + '</option><option value=2>' + "Clear the core" + '</option><option value=6>' + "Upload recovery core" + '</option><option value=7>' + "Upload tiny core" + '</option><option value=3>' + "Upload a core file" + '</option><option value=4>' + "Soft disconnect agent" + '</option><option value=5>' + "Hard disconnect agent" + '</option><option value=8>' + "Restart agent service" + '</select>')); }
else {
var htmlValue = '<select id=d3coreMode style=width:230px>' +
'<option value=1>' + "Upload default server core" + '</option>' +
'<option value=2>' + "Clear the core" + '</option>' +
'<option value=3>' + "Upload a core file" + '</option>' +
'<option value=4>' + "Soft disconnect agent" + '</option>' +
'<option value=5>' + "Hard disconnect agent" + '</option>' +
'<option value=6>' + "Upload recovery core" + '</option>' +
'<option value=7>' + "Upload tiny core" + '</option>' +
'<option value=8>' + "Restart agent service" + '</option>' +
'<option value=9>' + "Force agent update" + '</option></select>';
setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", htmlValue));
}
}
function p15uploadCoreEx() {
@ -6816,6 +6859,9 @@
} else if (Q('d3coreMode').value == 8) {
// Restart MeshAgent service
meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value:'service restart' });
} else if (Q('d3coreMode').value == 9) {
// Update mesh agent
meshserver.send({ action: 'updateAgents', nodeids: [consoleNode._id] });
}
}
@ -7574,7 +7620,7 @@
function addLink(x, f) { return '<a style=cursor:pointer;text-decoration:none onclick=\'' + f + '\'>&diams; ' + x + '</a>'; }
function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
function addKeyLink(x, f) { return '<span tabindex=0 style=cursor:pointer;text-decoration:none onclick=' + f + ' onkeypress="if (event.key==\'Enter\') { ' + f + ' } ">' + x + ' <img class=hoverButton src=images/key16.png></span>'; }
function addKeyLinkConditional(x, f, c) { if (c) return addKeyLink(x, f); return x; }
function addKeyLinkConditional(x, t, c) { if (c) return '<span title=\'' + t + '\'>' + x + ' <img class=hoverButton src=images/key16.png></span>'; return x }
function passwordcheck(p) { var re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()]).{8,}/; return re.test(p); }
function getFileSizeStr(size) { if (typeof size != 'number') { size = 0; } if (size == 1) return "1 byte"; return format('{0} bytes', size); }
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }

View file

@ -164,6 +164,9 @@
<div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="display: none;" title="Click to view current notifications">0</div>
</div>
<p id="logoutControl"><span id=logoutControlSpan class="logoncontrolspan"></span><span id=idleTimeoutNotify style="color:yellow"></span></p>
<div class=textnewui id=textnewui onmouseup=toggleBootstrapUIMode() onkeypress="if (event.key=='Enter') { toggleBootstrapUIMode(); }">
<b>Try the new MeshCentral UI</b>
</div>
</div>
<div id="page_leftbar">
<div style="height:16px"></div>
@ -199,11 +202,13 @@
<div tabindex=0 id=uiViewButton1 class=uiSelector onclick=userInterfaceSelectMenu(1) title="Left bar interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(1)"><div class="uiSelector1"></div></div>
<div tabindex=0 id=uiViewButton2 class=uiSelector onclick=userInterfaceSelectMenu(2) title="Top bar interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(2)"><div class="uiSelector2"></div></div>
<div tabindex=0 id=uiViewButton3 class=uiSelector onclick=userInterfaceSelectMenu(3) title="Fixed width interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(3)"><div class="uiSelector3"></div></div>
<div tabindex=0 id=uiViewButton7 class=uiSelector onclick=toggleBootstrapUIMode() title="Toggle Modern UI" onkeypress="if (event.key == 'Enter') toggleBootstrapUIMode()"><div class="uiSelector7"></div></div>
</td>
<td>
<div tabindex=0 id=uiViewButton6 class=uiSelector onclick="showNotes(false)" title="Personal Notes" onkeypress="if (event.key == 'Enter') showNotes(false)"><div class="uiSelector6"></div></div>
<div tabindex=0 id=uiViewButton4 class=uiSelector onclick=toggleNightMode() title="Toggle night mode" onkeypress="if (event.key == 'Enter') toggleNightMode()"><div class="uiSelector4"></div></div>
<div tabindex=0 id=uiViewButton5 class=uiSelector onclick=toggleFooterBarMode() title="Toggle footer bar" onkeypress="if (event.key == 'Enter') toggleFooterBarMode()"><div class="uiSelector5"></div></div>
<div class=uiSelector_end>&nbsp;</div>
</td>
</tr>
</table>
@ -476,23 +481,23 @@
<td class="auto-style1">
Filter
<select id=p3filterevents onchange=refreshEvents()>
<option value="">All Logs</option>
<option value=agentlog>Agent Logs</option>
<option value=relaylog>Relay Logs</option>
<option value=manual>Manual Logs</option>
<option value=runcommands>Run Command Logs</option>
<option value=batchupload>Batch Upload Logs</option>
<option value=changenode>Change Node Logs</option>
<option value=removenode>Remove Node Logs</option>
<option notransval=1 value="">All Logs</option>
<option notransval=1 value=agentlog>Agent Logs</option>
<option notransval=1 value=relaylog>Relay Logs</option>
<option notransval=1 value=manual>Manual Logs</option>
<option notransval=1 value=runcommands>Run Command Logs</option>
<option notransval=1 value=batchupload>Batch Upload Logs</option>
<option notransval=1 value=changenode>Change Node Logs</option>
<option notransval=1 value=removenode>Remove Node Logs</option>
</select>
Show
<select id=p3limitdropdown onchange=refreshEvents()>
<option value=60>Last 60</option>
<option value=120>Last 120</option>
<option value=250>Last 250</option>
<option value=500>Last 500</option>
<option value=1000>Last 1000</option>
<option value="">No limit</option>
<option notransval=1 value=60>Last 60</option>
<option notransval=1 value=120>Last 120</option>
<option notransval=1 value=250>Last 250</option>
<option notransval=1 value=500>Last 500</option>
<option notransval=1 value=1000>Last 1000</option>
<option notransval=1 value="">No limit</option>
</select>&nbsp;
<a href=# onclick=p3showDownloadEventsDialog(2)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
</td>
@ -993,22 +998,22 @@
<td class="auto-style1">
Filter
<select id=p16filterevents onchange=refreshDeviceEvents()>
<option value="">All Logs</option>
<option value=agentlog>Agent Logs</option>
<option value=relaylog>Relay Logs</option>
<option value=manual>Manual Logs</option>
<option value=runcommands>Run Command Logs</option>
<option value=batchupload>Batch Upload Logs</option>
<option value=changenode>Change Node Logs</option>
<option value=removenode>Remove Node Logs</option>
<option notransval=1 value="">All Logs</option>
<option notransval=1 value=agentlog>Agent Logs</option>
<option notransval=1 value=relaylog>Relay Logs</option>
<option notransval=1 value=manual>Manual Logs</option>
<option notransval=1 value=runcommands>Run Command Logs</option>
<option notransval=1 value=batchupload>Batch Upload Logs</option>
<option notransval=1 value=changenode>Change Node Logs</option>
<option notransval=1 value=removenode>Remove Node Logs</option>
</select>
Show
<select id=p16limitdropdown onchange=refreshDeviceEvents()>
<option value=60>Last 60</option>
<option value=120>Last 120</option>
<option value=250>Last 250</option>
<option value=500>Last 500</option>
<option value=1000>Last 1000</option>
<option notransval=1 value=60>Last 60</option>
<option notransval=1 value=120>Last 120</option>
<option notransval=1 value=250>Last 250</option>
<option notransval=1 value=500>Last 500</option>
<option notransval=1 value=1000>Last 1000</option>
</select>
<a href=# onclick=p3showDownloadEventsDialog(1)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
</td>
@ -1133,23 +1138,23 @@
<td class="auto-style1">
Filter
<select id=p31filterevents onchange=refreshUsersEvents()>
<option value="">All Logs</option>
<option value=agentlog>Agent Logs</option>
<option value=relaylog>Relay Logs</option>
<option value=manual>Manual Logs</option>
<option value=runcommands>Run Command Logs</option>
<option value=batchupload>Batch Upload Logs</option>
<option value=changenode>Change Node Logs</option>
<option value=removenode>Remove Node Logs</option>
<option notransval=1 value="">All Logs</option>
<option notransval=1 value=agentlog>Agent Logs</option>
<option notransval=1 value=relaylog>Relay Logs</option>
<option notransval=1 value=manual>Manual Logs</option>
<option notransval=1 value=runcommands>Run Command Logs</option>
<option notransval=1 value=batchupload>Batch Upload Logs</option>
<option notransval=1 value=changenode>Change Node Logs</option>
<option notransval=1 value=removenode>Remove Node Logs</option>
</select>
Show
<select id=p31limitdropdown onchange=refreshUsersEvents()>
<option value=60>Last 60</option>
<option value=120>Last 120</option>
<option value=250>Last 250</option>
<option value=500>Last 500</option>
<option value=1000>Last 1000</option>
<option value="">No limit</option>
<option notransval=1 value=60>Last 60</option>
<option notransval=1 value=120>Last 120</option>
<option notransval=1 value=250>Last 250</option>
<option notransval=1 value=500>Last 500</option>
<option notransval=1 value=1000>Last 1000</option>
<option notransval=1 value="">No limit</option>
</select>
<a href=# onclick=p3showDownloadEventsDialog(3)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
</td>
@ -1453,16 +1458,16 @@
<div style="margin-top:8px">
<div>Display Size</div>
<select id="d7rdpsize">
<option value="canvas">Canvas Size</option>
<option value="browser">Browser Size</option>
<option value="screen">Screen Size</option>
<option value="640x480">640x480</option>
<option value="1024x768">1024x768</option>
<option value="1280x800">1280x800</option>
<option value="1440x900">1440x900</option>
<option value="1600x900">1600x900</option>
<option value="1680x1050">1680x1050</option>
<option value="1920x1080">1920x1080</option>
<option notransval=1 value="canvas">Canvas Size</option>
<option notransval=1 value="browser">Browser Size</option>
<option notransval=1 value="screen">Screen Size</option>
<option notransval=1 value="640x480">640x480</option>
<option notransval=1 value="1024x768">1024x768</option>
<option notransval=1 value="1280x800">1280x800</option>
<option notransval=1 value="1440x900">1440x900</option>
<option notransval=1 value="1600x900">1600x900</option>
<option notransval=1 value="1680x1050">1680x1050</option>
<option notransval=1 value="1920x1080">1920x1080</option>
</select>
</div>
<div>
@ -1474,7 +1479,7 @@
<label style="display:block"><input type="checkbox" id="d7rdp4" />Disable Theming</label>
<label style="display:block"><input type="checkbox" id="d7rdp6" />Disable Cursor Shadow</label>
<label style="display:block"><input type="checkbox" id="d7rdp7" />Disable Cursor Settings</label>
<label style="display:block"><input type="checkbox" id="d7rdp8" />Enable Font Smooting</label>
<label style="display:block"><input type="checkbox" id="d7rdp8" />Enable Font Smoothing</label>
<label style="display:block"><input type="checkbox" id="d7rdp9" />Enable Desktop Composision</label>
<label style="display:block"><input type="checkbox" id="d7rdpclip" />Automatic Clipboard</label>
<label style="display:block"><input type="checkbox" id="d7rdpsmb" />Swap Mouse Buttons</label>
@ -1548,8 +1553,8 @@
var sessionTime = parseInt('{{{sessiontime}}}');
var webRelayPort = parseInt('{{{webRelayPort}}}');
var webRelayDns = '{{{webRelayDns}}}';
var hidePowerTimeline = {{{hidePowerTimeline}}};
var showNotesPanel = {{{showNotesPanel}}};
var hidePowerTimeline = '{{{hidePowerTimeline}}}';
var showNotesPanel = '{{{showNotesPanel}}}';
var sessionRefreshTimer = null;
var domain = '{{{domain}}}';
var domainUrl = '{{{domainurl}}}';
@ -1642,6 +1647,7 @@
delete urlargs.viewmode;
delete urlargs.gotonode;
delete urlargs.gotodevicename;
delete urlargs.gotodeviceip;
delete urlargs.gotomesh;
delete urlargs.gotouser;
delete urlargs.gotougrp;
@ -1722,6 +1728,9 @@
});
}
// Show the modern ui switcher
QV('textnewui', ((features2 & 0x40000000) == 0) ? false : true);
// Connect to the mesh server
meshserver = MeshServerCreateControl(domainUrl);
meshserver.onStateChanged = onStateChanged;
@ -2182,6 +2191,24 @@
QV('body', true);
}
function saveUserInterfaceMode() {
var nUiViewMode = 2;
if (Q('ui1').checked) { nUiViewMode = 3; }
if (getstore('uiViewMode', 2) != nUiViewMode) {
putstore('uiViewMode', nUiViewMode);
reload();
}
}
function toggleBootstrapUIMode() {
if (xxdialogMode) return;
var uiViewMode = getstore('uiViewMode', 2);
var x = '<input type=radio id=ui0 name=uiradio value=2 ' + ((uiViewMode == 2)?'checked':'') + '><label for=ui0>' + "Classic" + '</label><br>';
x += '<input type=radio id=ui1 name=uiradio value=3 ' + ((uiViewMode == 3)?'checked':'') + '><label for=ui1>' + "Modern" + '</label><br>';
setDialogMode(2, "User Interface", 3, saveUserInterfaceMode, x);
QV('uiMenu', false);
}
function getNodeFromId(id) { if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } } return null; }
function reload() {
var x = window.location.href;
@ -2420,15 +2447,28 @@
files.Stop();
files = null;
}
window.location.href = 'logout';
if (serverinfo.logoutonidlesessiontimeout) {
window.location.href = 'logout';
}
} else {
var ds = Math.round((serverinfo.timeout - delta) / 1000);
var sessionInProgress = desktop != null || terminal != null || files != null;
var show = serverinfo.logoutonidlesessiontimeout || sessionInProgress;
var isLogout = serverinfo.logoutonidlesessiontimeout;
var theText = ''; // Initialize theText
if (ds <= 60) {
QH('idleTimeoutNotify', '<br />' + format((ds == 1)?"1 second until disconnect":"{0} seconds until disconnect", ds));
theText = isLogout
? (ds == 1 ? "1 second until logout" : "{0} seconds until logout")
: (ds == 1 ? "1 second until disconnect" : "{0} seconds until disconnect");
} else {
ds = Math.round(ds / 60);
if (ds <= 5) { QH('idleTimeoutNotify', '<br />' + format((ds == 1)?"1 minute until disconnect":"{0} minutes until disconnect", ds)); }
if (ds <= 5) {
theText = isLogout
? (ds == 1 ? "1 minute until logout" : "{0} minutes until logout")
: (ds == 1 ? "1 minute until disconnect" : "{0} minutes until disconnect");
}
}
QH('idleTimeoutNotify', show && theText ? '<br />' + format(theText, ds) : '');
}
}
@ -2837,11 +2877,11 @@
if (net.name) { x += addHtmlValue2("Name", '<b>' + EscapeHtml(net.name) + '</b>'); }
if (net.desc) { x += addHtmlValue2("Description", EscapeHtml(net.desc).replace('(R)', '&reg;').replace('(r)', '&reg;')); }
if (net.dnssuffix) { x += addHtmlValue2("DNS suffix", EscapeHtml(net.dnssuffix) + ' <img src="images/link4.png" title="' + "Copy name to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.dnssuffix) + '") width=10 height=10>'); }
if (net.mac) { x += addHtmlValue2("MAC address", '<a href="https://dnslytics.com/mac-address-lookup/' + net.mac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.mac.toLowerCase()) + '") width=10 height=10>'); }
if (net.mac) { x += addHtmlValue2("MAC address", '<a href="https://maclookup.app/search/result?mac=' + net.mac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.mac.toLowerCase()) + '") width=10 height=10>'); }
if (net.v4addr) { x += addHtmlValue2("IPv4 address", EscapeHtml(net.v4addr) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.v4addr) + '") width=10 height=10>'); }
if (net.v4mask) { x += addHtmlValue2("IPv4 mask", EscapeHtml(net.v4mask) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.v4mask) + '") width=10 height=10>'); }
if (net.v4gateway) { x += addHtmlValue2("IPv4 gateway", EscapeHtml(net.v4gateway) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.v4gateway) + '") width=10 height=10>'); }
if (net.gatewaymac) { x += addHtmlValue2("Gateway MAC", '<a href="https://dnslytics.com/mac-address-lookup/' + net.gatewaymac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.gatewaymac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.gatewaymac.toLowerCase()) + '") width=10 height=10>'); }
if (net.gatewaymac) { x += addHtmlValue2("Gateway MAC", '<a href="https://maclookup.app/search/result?mac=' + net.gatewaymac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.gatewaymac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.gatewaymac.toLowerCase()) + '") width=10 height=10>'); }
}
} else if (message.netif2 != null) {
// New style
@ -2850,7 +2890,7 @@
if ((Array.isArray(net) == false) || (net.length < 1) || (net[0] == null) || ((typeof net[0].mac == 'string') && (net[0].mac.startsWith('00:00:00:00')))) continue;
x += '<hr />'
x += addHtmlValue2("Name", '<b>' + EscapeHtml(i) + '</b>');
if (typeof net[0].mac == 'string') { x += addHtmlValue2("MAC address", '<a href="https://dnslytics.com/mac-address-lookup/' + net[0].mac.split(':').join('').substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net[0].mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net[0].mac.toLowerCase()) + '") width=10 height=10>'); }
if (typeof net[0].mac == 'string') { x += addHtmlValue2("MAC address", '<a href="https://maclookup.app/search/result?mac=' + net[0].mac.split(':').join('').substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net[0].mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net[0].mac.toLowerCase()) + '") width=10 height=10>'); }
if (net[0].fqdn) { x += addHtmlValue2("FQDN", net[0].fqdn); }
for (var j = 0; j < net.length; j++) {
var netif = net[j];
@ -2998,7 +3038,7 @@
}
}else{
Q('notesPanelArea').innerHTML = (message.notes && marked && DOMPurify) ? DOMPurify.sanitize(marked.parse(decodeURIComponent(message.notes), { breaks: true }), { USE_PROFILES: { html: true } }) : '';
if (showNotesPanel && message.notes) { QV('notesPanel',true); }else{ QV('notesPanel', false); }
if ((showNotesPanel === 'true') && message.notes) { QV('notesPanel',true); }else{ QV('notesPanel', false); }
}
break;
}
@ -3011,7 +3051,7 @@
var secret = message.secret;
if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
QH('d2optinfo', '<table style=width:380px><tr><td style=vertical-align:top>' + format("Install <a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\" rel=\"noreferrer noopener\" target=_blank>Google Authenticator</a> or a compatible application and scan the barcode, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank>this link</a> or enter the secret. Then, enter the current 6 digit token below to activate 2-Step login.", message.url) + '<br /><br />' + 'Secret <img src=images/link4.png height=10 width=10 title="' + "Copy Secret to clipboard" + '" style=cursor:pointer onclick=d2CopySecretToClip()>' + '<br /><tt id=d2optsecret secret="' + message.secret + '" style=font-size:12px>' + secret + '</tt><br /><br /></td><td style=width:1px;vertical-align:top><a href="' + message.url + '" rel="noreferrer noopener" target=_blank><div id="qrcode"></div></a></td><tr><td colspan=2 style="text-align:center;border-top:1px solid black"><br />' + "Enter the token here for 2-step login:" + ' <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress="return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></td></table>');
QH('d2optinfo', '<table style=width:380px><tr><td style=vertical-align:top>' + format("Install" + ' <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" rel="noreferrer noopener" target=_blank>' + "Google Authenticator" + '</a> ' + "or a compatible application and scan the barcode, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank>this link</a> or enter the secret. Then, enter the current 6 digit token below to activate 2-Step login.", message.url) + '<br /><br />' + 'Secret <img src=images/link4.png height=10 width=10 title="' + "Copy Secret to clipboard" + '" style=cursor:pointer onclick=d2CopySecretToClip()>' + '<br /><tt id=d2optsecret secret="' + message.secret + '" style=font-size:12px>' + secret + '</tt><br /><br /></td><td style=width:1px;vertical-align:top><a href="' + message.url + '" rel="noreferrer noopener" target=_blank><div id="qrcode"></div></a></td><tr><td colspan=2 style="text-align:center;border-top:1px solid black"><br />' + "Enter the token here for 2-step login:" + ' <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress="return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></td></table>');
new QRCode(Q('qrcode'), { text: message.url, width: 128, height: 128, colorDark: '#000000', colorLight: '#EEE', correctLevel: QRCode.CorrectLevel.H });
QV('idx_dlgOkButton', true);
QE('idx_dlgOkButton', false);
@ -3066,7 +3106,7 @@
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
var start = '<div style="border-radius:6px;border:2px solid #CCC;background-color:#BBB;width:100%;box-sizing:border-box;margin-bottom:6px"><div style="margin:3px;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:bold"><table style=width:100%;text-align:left>';
var end = '</table></div></div>';
var x = "<a href=\"https://www.yubico.com/\" rel=\"noreferrer noopener\" target=\"_blank\">Hardware keys</a> are used as secondary login authentication.";
var x = '<a href="https://www.yubico.com/" rel="noreferrer noopener" target="_blank">' + "Hardware keys" + '</a> ' + "are used as secondary login authentication.";
x += '<div style="max-height:150px;overflow-y:auto;overflow-x:hidden;margin-top:6px;margin-bottom:6px">';
if (message.keys && message.keys.length > 0) {
for (var i in message.keys) {
@ -3502,6 +3542,7 @@
// Change the node
node.name = message.event.node.name;
node.rname = message.event.node.rname;
node.lusers = message.event.node.lusers;
node.users = message.event.node.users;
node.host = message.event.node.host;
node.desc = message.event.node.desc;
@ -4018,6 +4059,10 @@
var foundNode = null;
if (nodes != null) { for (var i in nodes) { if (nodes[i].name == args.gotodevicename) { foundNode = nodes[i]._id; } } }
if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
} else if (args.gotodeviceip != null) {
var foundNode = null;
if (nodes != null) { for (var i in nodes) { if (nodes[i].ip == args.gotodeviceip) { foundNode = nodes[i]._id; } } }
if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
} else if (args.gotomesh != null) {
if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet
gotoMesh('mesh/' + domain + '/' + args.gotomesh);
@ -4898,13 +4943,13 @@
r += '<span style=line-height:20px>' + groupingTags + '</span>';
}
if (deviceViewSettings.devsCols.indexOf('windowsav') >= 0) { // Windows AV
r += '<td style=text-align:center>' + ((node.wsc && node.wsc.antiVirus != null) ? (node.wsc.antiVirus == 'OK' ? "<span style=color:green>OK</span>" : "<span style=color:red>BAD</span>") : "");
r += '<td style=text-align:center>' + ((node.wsc && node.wsc.antiVirus != null) ? (node.wsc.antiVirus == 'OK' ? '<span style=color:green>' + "OK" + '</span>' : '<span style=color:red>' + "BAD" + '</span>') : "");
}
if (deviceViewSettings.devsCols.indexOf('windowsupdate') >= 0) {// Windows Update
r += '<td style=text-align:center>' + ((node.wsc && node.wsc.autoUpdate != null) ? (node.wsc.autoUpdate == 'OK' ? "<span style=color:green>OK</span>" : "<span style=color:red>BAD</span>") : "");
r += '<td style=text-align:center>' + ((node.wsc && node.wsc.autoUpdate != null) ? (node.wsc.autoUpdate == 'OK' ? '<span style=color:green>' + "OK" + '</span>' : '<span style=color:red>' + "BAD" + '</span>') : "");
}
if (deviceViewSettings.devsCols.indexOf('windowsfirewall') >= 0) { // Windows Firewall
r += '<td style=text-align:center>' + ((node.wsc && node.wsc.firewall != null) ? (node.wsc.firewall == 'OK' ? "<span style=color:green>OK</span>" : "<span style=color:red>BAD</span>") : "");
r += '<td style=text-align:center>' + ((node.wsc && node.wsc.firewall != null) ? (node.wsc.firewall == 'OK' ? '<span style=color:green>' + "OK" + '</span>' : '<span style=color:red>' + "BAD" + '</span>') : "");
}
if (deviceViewSettings.devsCols.indexOf('lastbootuptime') >= 0) { // Last Boot Up Time
r += '<td style=text-align:center;font-size:x-small>' + ((node.lastbootuptime != null) ? printDateTime(new Date(node.lastbootuptime)) : "");
@ -5395,7 +5440,7 @@
x += addHtmlValue("New Password*", '<input id=dp1password1 type=password style=width:230px autocomplete=off maxlength=32 onchange=validateAmtAcmSetupEx() onkeyup=validateAmtAcmSetupEx() />');
x += addHtmlValue("New Password*", '<input id=dp1password2 type=password style=width:230px autocomplete=off maxlength=32 onchange=validateAmtAcmSetupEx() onkeyup=validateAmtAcmSetupEx() />');
if ((features2 & 0x00000020) && (currentMesh.mtype == 1) && (serverinfo.amtProvServerMeshId == currentMesh._id)) { x += '<label><input id=dp1lanprov type=checkbox /> ' + "Use for bare-metal LAN activation." + '</label>'; } // Intel AMT LAN provisioning server is active.
x += '<div><span id=dp10passNotify style="font-size:10px"> ' + "* 8 characters, 1 upper, 1 lower, 1 numeric, 1 non-alpha numeric." + '</span></div>';
x += '<div><span id=dp10passNotify style="font-size:10px"> ' + "* 8-16 characters, 1 upper, 1 lower, 1 numeric, 1 non-alpha numeric." + '</span></div>';
setDialogMode(2, "Intel&reg; AMT ACM", 3, showAmtAcmSetupEx, x);
Q('dp1password0').focus();
validateAmtAcmSetupEx();
@ -5614,10 +5659,10 @@
// QR code agent install
x += '<div id=agins_qrcode style=display:none;min-height:180px><a id=agins_qrimage_a rel=\"noreferrer noopener\" target=_blank><div id=agins_qrimage style=float:right;margin-left:10px;width:180px;height:180px;cursor:pointer></div></a><div>' + format("To add a mobile device to group \"{0}\", download the MeshAgent application and scan this QR code.", EscapeHtml(mesh.name)) + '</div>';
x += "<table style=width:180px>";
x += "<tr><td style=text-align:center><a rel=\"noreferrer noopener\" target=_blank href=\"https://play.google.com/store/apps/details?id=com.meshcentral.agent2\"><img style=cursor:pointer src=\"images/google-play-140.png\" width=140 srcset=\"images/google-play-280.png 2x\" /></a></td></tr>";
x += "<tr><td style=text-align:center><a rel=\"noreferrer noopener\" target=_blank href=\"https://www.amazon.co.uk/gp/product/B097Z4Q7SK/\"><img style=cursor:pointer src=\"images/amazon-appstore-140.png\" width=140 srcset=\"images/amazon-appstore-280.png 2x\" /></a></td></tr>";
x += "</table>";
x += '<table style=width:180px>';
x += '<tr><td style=text-align:center><a title="' + "Google Play Store" + '"rel="noreferrer noopener" target=_blank href="https://play.google.com/store/apps/details?id=com.meshcentral.agent2"><img style=cursor:pointer src="images/google-play-140.png" width=140 srcset="images/google-play-280.png 2x" /></a></td></tr>';
x += '<tr><td style=text-align:center><a title="' + "Amazon App Store" + '" rel="noreferrer noopener" target=_blank href="https://www.amazon.co.uk/gp/product/B097Z4Q7SK/"><img style=cursor:pointer src="images/amazon-appstore-140.png" width=140 srcset="images/amazon-appstore-280.png 2x" /></a></td></tr>';
x += '</table>';
x += addHtmlValue("Android APK", '<a onclick=downloadFile("meshagents?id=14' + (urlargs.key?('&key=' + urlargs.key):'') + '",null,true) title="' + "APK version of the MeshAgent" + '">' + "APK" + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy URL to clipboard" + '" style=cursor:pointer onclick=copyAgentUrl("meshagents?id=14&meshid=' + meshid.split('/')[2] + (urlargs.key?('&key=' + urlargs.key):'') + '")>');
x += '</div>'
@ -5942,7 +5987,7 @@
setDialogMode(2, "Edit Device Tags", 3, d2groupActionFunctionTagsExec, x);
} else if (op == 108) {
// Device notification
var x = "<div style=margin-bottom:4px>Perform batch device notification</div>";
var x = '<div style=margin-bottom:4px>'+ "Perform batch device notification" + '</div>';
x += '<select id=d2deviceop style=width:100%;margin-bottom:4px><option value=2>' + "Toast Notification" + '</option><option value=1>' + "Message Box" + '</option><option value=3>' + "Alert Box" + '</option></select>';
x += '<input id=dp2notifyTitle maxlength=256 placeholder="' + "Title" + '" style=width:100%;box-sizing:border-box;margin-bottom:4px />';
x += '<textarea id=d2notifyMsg style=background-color:#fcf3cf;width:100%;height:140px;resize:none;overflow-y:scroll;box-sizing:border-box;margin-bottom:4px></textarea>';
@ -6056,7 +6101,7 @@
meshserver.send({ action: 'getDeviceDetails', nodeids: chkNodeIds, tz: tz, tf: new Date().getTimezoneOffset(), l: getLang(), type: 'csv' }); // With details
} else {
// Without details
var csv = "id,name,rname,host,icon,ip,osdesc,state,groupname,conn,pwr,av,update,firewall,bitlocker,avdetails,tags" + '\r\n', r = [];
var csv = "id,name,rname,host,icon,ip,osdesc,state,groupname,conn,pwr,av,update,firewall,bitlocker,avdetails,tags,lastbootuptime" + '\r\n', r = [];
for (var i in chkNodeIds) {
var n = getNodeFromId(chkNodeIds[i]);
csv += '"' + n._id.split(',').join('') + '","' + n.name.split(',').join('') + '","' + (n.rname?(n.rname.split(',').join('')):'') + '","' + (n.host?(n.host.split(',').join('')):'') + '","' + n.icon + '","' + (n.ip?n.ip:'') + '","' + (n.osdesc?(n.osdesc.split(',').join('')):'') + '","' + n.state + '","' + meshes[n.meshid].name.split(',').join('') + '","' + (n.conn?n.conn:'') + '","' + (n.pwr?n.pwr:'') + '"';
@ -6082,6 +6127,7 @@
else {
csv += ',';
}
if (typeof n.lastbootuptime == 'number') { csv += ',"' + n.lastbootuptime + '"'; }
csv += '\r\n';
}
saveAs(stringToUtf8Blob(csv), "devicelist.csv");
@ -7616,7 +7662,12 @@
}
// Active Users
if (node.users && (node.users.length > 0)) { x += addDeviceAttribute(((node.users.length > 1)?"Active Users":"Active User"), EscapeHtml(node.users.join(', '))); }
if (node.users && node.users.length > 0) {
var u = node.users.map(function(user) {
return addKeyLinkConditional(EscapeHtml(user), "Locked", (node.lusers && node.lusers.indexOf(user) >= 0));
}).join(', ');
x += addDeviceAttribute((node.users.length > 1 ? "Active Users" : "Active User"), u);
}
// Display device user consent
if ((node.agent != null) && (node.agent.id != 14) && (node.mtype != 3)) {
@ -8203,7 +8254,7 @@
if (noteid == null) { noteid = encodeURIComponentEx('p'+userinfo._id); }
var x = '<textarea id=d2devNotes ro=' + readonly + ' noteid=' + noteid + ' readonly style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
if (noteid.startsWith('node%2F%2F')) { x += ' <span style=font-size:10px>' + "Device group notes can be viewed and changed by other device group administrators." + '</span>'; }
if (showNotesPanel) { x += ' <span style=font-size:10px><a target=_blank href=\'https://www.markdownguide.org/cheat-sheet/\'>' + "Markdown syntax supported" + '</a></span>'; }
if (showNotesPanel === 'true') { x += ' <span style=font-size:10px><a target=_blank href=\'https://www.markdownguide.org/cheat-sheet/\'>' + "Markdown syntax supported" + '</a></span>'; }
setDialogMode(2, "Notes", 3, showNotesEx, x, noteid);
meshserver.send({ action: 'getNotes', id: decodeURIComponent(noteid) });
}
@ -8211,7 +8262,7 @@
function showNotesEx(buttons, tag) {
Q('notesPanelArea').innerHTML = (marked && DOMPurify) ? DOMPurify.sanitize(marked.parse(Q('d2devNotes').value, { breaks: true }), { USE_PROFILES: { html: true } }) : Q('d2devNotes').value;
meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponentEx(Q('d2devNotes').value) });
if (showNotesPanel && Q('d2devNotes').value != '') { QV('notesPanel',true); }else{ QV('notesPanel', false); }
if ((showNotesPanel === 'true') && Q('d2devNotes').value != '') { QV('notesPanel',true); }else{ QV('notesPanel', false); }
}
function openIpKvmRemoteControl(nodeid) {
@ -8632,7 +8683,7 @@
// Draw device power bars. The bars are 766px wide.
function drawDeviceTimeline() {
if ((currentNode == null) || (xxcurrentView < 10) || (xxcurrentView > 19) || (currentNode.mtype == 3) || (hidePowerTimeline)) return;
if ((currentNode == null) || (xxcurrentView < 10) || (xxcurrentView > 19) || (currentNode.mtype == 3) || (hidePowerTimeline === 'true')) return;
var timeline = null, now = Date.now();
if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
@ -11316,7 +11367,7 @@
}
function p13openfilefolder() {
setDialogMode(2, "Open File/Folder", 3, p13openfilefolderEx, "Are you sure you want to open this file/folder on the remote devices desktop ?");
setDialogMode(2, "Open File/Folder", 3, p13openfilefolderEx, "Are you sure you want to open this file/folder on the remote devices desktop?");
}
function p13openfilefolderEx() {
var openfilefolder = "", checkboxes = document.getElementsByName('fd');
@ -12231,7 +12282,7 @@
}
if (hardware.network && hardware.network.dns) {
x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
x += addDetailItem("<b>DNS Servers</b>", hardware.network.dns.join(", "));
x += addDetailItem('<b>' + "DNS Servers" + '</b>', hardware.network.dns.join(", "));
x += '</div></td></tr>';
}
x += '</table>';
@ -12693,7 +12744,19 @@
if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeids: [ consoleNode._id ], type: 'default' }); } // Upload default core
else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeids: [ consoleNode._id ], type: 'clear' }); } // Clear the core
else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file
else { setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", '<select id=d3coreMode style=width:230px><option value=1>' + "Upload default server core" + '</option><option value=2>' + "Clear the core" + '</option><option value=6>' + "Upload recovery core" + '</option><option value=7>' + "Upload tiny core" + '</option><option value=3>' + "Upload a core file" + '</option><option value=4>' + "Soft disconnect agent" + '</option><option value=5>' + "Hard disconnect agent" + '</option><option value=8>' + "Restart agent service" + '</select>')); }
else {
var htmlValue = '<select id=d3coreMode style=width:230px>' +
'<option value=1>' + "Upload default server core" + '</option>' +
'<option value=2>' + "Clear the core" + '</option>' +
'<option value=3>' + "Upload a core file" + '</option>' +
'<option value=4>' + "Soft disconnect agent" + '</option>' +
'<option value=5>' + "Hard disconnect agent" + '</option>' +
'<option value=6>' + "Upload recovery core" + '</option>' +
'<option value=7>' + "Upload tiny core" + '</option>' +
'<option value=8>' + "Restart agent service" + '</option>' +
'<option value=9>' + "Force agent update" + '</option></select>';
setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", htmlValue));
}
}
function p15uploadCoreEx() {
@ -12721,6 +12784,9 @@
} else if (Q('d3coreMode').value == 8) {
// Restart MeshAgent service
meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value:'service restart' });
} else if (Q('d3coreMode').value == 9) {
// Update mesh agent
meshserver.send({ action: 'updateAgents', nodeids: [ consoleNode._id ] });
}
}
@ -13407,7 +13473,7 @@
function server_showRestoreDlg() {
if (xxdialogMode) return false;
var x = "Restore the server using a backup, <span style=color:red>this will delete the existing server data</span>. Only do this if you know what you are doing." + '<br /><br />';
var x = "Restore the server using a backup," + ' <span style=color:red>' + "this will delete the existing server data." + '</span> ' + "Only do this if you know what you are doing." + '<br /><br />';
x += '<form action="/restoreserver.ashx' + ((urlargs.key)?('?key=' + urlargs.key):'') + '" enctype="multipart/form-data" method="post"><div>';
x += '<input type=hidden name=auth value=' + authCookie + '>';
x += '<input id=account_dlgFileInput type=file name=datafile style=width:100% accept=".zip,application/octet-stream,application/zip,application/x-zip,application/x-zip-compressed" onchange=account_validateServerRestore()><br /><br />';
@ -13839,7 +13905,7 @@
x += addHtmlValue("Unknown password", '<select id=dp20amtbadpass style=width:230px><option value=0>' + "Do nothing" + '</option><option value=1>' + "If in CCM, reactivate Intel&reg; AMT" + '</option></select>');
x += '</div>';
if ((features & 0x400) == 0) { x += addHtmlValue('<span title="' + "Client Initiated Remote Access" + '">' + "CIRA setup" + '</span>', '<select id=dp20amtcira style=width:230px><option value=0>' + "Do nothing" + '</option><option value=1>' + "Don't connect to server" + '</option><option value=2>' + "Connect to server" + '</option></select>'); }
x += '<span id=dp10passNotify style="font-size:10px"> ' + "* 8 characters, 1 upper, 1 lower, 1 numeric, 1 non-alpha numeric." + '</span>';
x += '<span id=dp10passNotify style="font-size:10px"> ' + "* 8-16 characters, 1 upper, 1 lower, 1 numeric, 1 non-alpha numeric." + '</span>';
if ((currentMesh.mtype == 2) && (ptype == 2)) { x += '<span style="font-size:10px"> ' + "This policy will not impact devices with Intel&reg; AMT in ACM mode." + '</span>'; }
}
if (ptype == 0) { x = '<table style=padding-top:4px><tr><td><img style=padding-right:8px src=images/rcheckbox60.png width=60 height=60><td>' + "When this policy is selected, Intel&reg; AMT is not managed by this server. Intel AMT can still be used by manually activating and configuring it." + '</table>'; }
@ -15867,7 +15933,7 @@
}
function p4downloadUserInfoCSV() {
var csv = "id, name, email, creation, lastlogin, groups, authfactors, siteadmin, useradmin, locked" + '\r\n';
var csv = "id,name,email,creation,lastlogin,groups,authfactors,siteadmin,useradmin,locked" + '\r\n';
for (var i in users) {
var multiFactor = false, factors = [];
if ((users[i].otpsecret > 0) || (users[i].otphkeys > 0)) {
@ -19161,7 +19227,7 @@
function addLink(x, f) { return '<span tabindex=0 style=cursor:pointer;text-decoration:none onclick=\'' + f + '\' onkeypress="if (event.key==\'Enter\') {' + f + '} ">' + x + ' <img class=hoverButton src=images/link5.png></span>'; }
function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
function addKeyLink(x, f) { return '<span tabindex=0 style=cursor:pointer;text-decoration:none onclick=' + f + ' onkeypress="if (event.key==\'Enter\') { ' + f + ' } ">' + x + ' <img class=hoverButton src=images/key16.png></span>'; }
function addKeyLinkConditional(x, f, c) { if (c) return addKeyLink(x, f); return x; }
function addKeyLinkConditional(x, t, c) { if (c) return '<span title=\'' + t + '\'>' + x + ' <img class=hoverButton src=images/key16.png></span>'; return x }
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
function addOption(q, t, i) { var option = document.createElement('option'); option.text = t; option.value = i; Q(q).add(option); }
function passwordcheck(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
@ -19271,4 +19337,4 @@
</script>
</body>
</html>
</html>

File diff suppressed because it is too large Load diff

View file

@ -412,7 +412,7 @@
var showLanguageSelect = '{{{showLanguageSelect}}}';
function startup() {
if (decodeURIComponent('{{{loginpicture}}}') == 'true') { Q('loginPicture').src = "loginlogo.png"; }
if (decodeURIComponent('{{{loginpicture}}}') == 'true') { Q('loginPicture').src = 'loginlogo.png'; }
QV('welcomeTextRow', welcomeText != '');
QH('welcomeText', welcomeText);
@ -460,7 +460,7 @@
// Show Language Select Box if needed
if (showLanguageSelect === 'top' || showLanguageSelect === 'bottom') {
var x = '<select id=d2langselect style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px" onChange="changeLanguage()">';
x += '<option value="*">Use Browser Language</option>';
x += '<option value="*">' + "Use Browser Language" + '</option>';
for (var i in serverLangs) {
var lang = serverLangs[i];
x += '<option value="' + lang + '"' + ((urlargs.lang == lang)?' selected':'') + '>' + lang + ' - ' + (loclist[lang]?loclist[lang]:loclistex[lang]) + '</option>';

View file

@ -1097,7 +1097,6 @@
if (term != null) { term.dispose(); }
term = new Terminal();
term.open(Q('XTermParent'));
term.onData(function (data) { if (tunnel != null) { tunnel.sendText(data); } })
term.resize(80, 25);
//term.setOption('convertEol', true); // Consider \n to be \r\n, this should be taken care of by "termios"
}

View file

@ -746,7 +746,7 @@
var domainUrl = '{{{domainurl}}}';
var authCookie = '{{{authCookie}}}';
var authRelayCookie = '{{{authRelayCookie}}}';
var viewOnly = (parseInt('{{{viewOnly}}}') == 1);
var viewOnly = parseInt('{{{viewOnly}}}');
var authCookieRenewTimer = null;
var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
var debugmode = false;
@ -820,7 +820,26 @@
}
function setSessionActivity() { sessionActivity = Date.now(); }
function checkIdleSessionTimeout() { var delta = (Date.now() - sessionActivity); if (delta > serverinfo.timeout) { window.location.href = 'logout'; } }
function checkIdleSessionTimeout() {
var delta = (Date.now() - sessionActivity);
if (delta > serverinfo.timeout) {
if (desktop != null) { // Disconnect remote desktop
desktop.Stop();
desktopNode = desktop = null;
}
if (terminal != null) { // Disconnect terminal
terminal.Stop();
terminal = null;
}
if (files != null) { // Disconnect files
files.Stop();
files = null;
}
if (serverinfo.logoutonidlesessiontimeout) {
window.location.href = 'logout';
}
}
}
//
// Menu System
@ -845,7 +864,7 @@
gotKeyPressEvent = true;
Q('softKeyboard').value = '';
// Check what keys we are allows to send
if (viewOnly) return false;
if (viewOnly == 1) return false;
return desktop.m.handleKeys(e);
}
if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (t !== 1)) {
@ -873,7 +892,7 @@
gotKeyPressEvent = false;
Q('softKeyboard').value = '';
// Check what keys we are allows to send
if (viewOnly) return false;
if (viewOnly == 1) return false;
return desktop.m.handleKeyDown(e);
}
if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (t !== 1)) {
@ -902,7 +921,7 @@
var inputStr = Q('softKeyboard').value;
Q('softKeyboard').value = '';
// Check what keys we are allows to send
if (viewOnly) return;
if (viewOnly == 1) return;
if ((gotKeyPressEvent == false) && (inputStr.length > 0) && desktop.m.SendKeyUnicode) {
// This is a mobile keyboard, we need to send that is in the input control.
var inputchar = inputStr[inputStr.length - 1].charCodeAt(0);
@ -1062,6 +1081,7 @@
desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'), authCookie);
desktop.debugmode = debugmode;
desktop.onStateChanged = onDesktopStateChange;
desktop.m.stopInput = (viewOnly == 1);
desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
desktop.m.useZRLE = (desktopsettings.encoding < 3);
desktop.m.showmouse = true;
@ -1071,6 +1091,8 @@
} else if ((contype == null) || (contype == 1) || ((contype == 3))) {
// Setup the Mesh Agent remote desktop
desktop = CreateAgentRedirect(null, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, null, domainUrl);
desktop.m.stopInput = (viewOnly == 1);
desktop.m.mouseCursorActive(true);
desktop.debugmode = debugmode;
desktop.m.debugmode = debugmode;
desktop.attemptWebRTC = attemptWebRTC;
@ -1442,7 +1464,7 @@
QV('deskarea4', !fullscreen);
QV('termarea1', !fullscreen);
QV('termarea4', !fullscreen);
var inputAllowed = !viewOnly;
var inputAllowed = (viewOnly == 0);
// Show full screen buttons if needed
QV('deskkeybutton1', fullscreen);

View file

@ -2837,6 +2837,38 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
res.set('Content-Type', 'text/html');
let url = domain.url;
if (Object.keys(req.query).length > 0) { url += "?" + Object.keys(req.query).map(function(key) { return encodeURIComponent(key) + "=" + encodeURIComponent(req.query[key]); }).join("&"); }
// check for relaystate is set, test against configured server name and accepted query params
if(req.body && req.body.RelayState !== undefined){
var relayState = decodeURIComponent(req.body.RelayState);
var serverName = (obj.getWebServerName(domain, req)).replaceAll('.','\\.');
var regexstr = `(?<=https:\\/\\/(?:.+?\\.)?${serverName}\\/?)` +
`.*((?<=([\\?&])gotodevicename=(.{64})|` +
`gotonode=(.{64})|` +
`gotodeviceip=(((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4})|` +
`gotodeviceip=(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:)` +
`lang=(.{5})|` +
`sitestyle=(\\d+)|` +
`user=(.{64})|` +
`pass=(.{256})|` +
`key=|` +
`locale=|` +
`gotomesh=(.{64})|` +
`gotouser=(.{0,64})|` +
`gotougrp=(.{64})|` +
`debug=|` +
`filter=|` +
`webrtc=|` +
`hide=|` +
`viewmode=(\\d+)(?=[\\&]|\\b)))`;
var regex = new RegExp(regexstr);
if(regex.test(relayState)){
url = relayState;
}
}
res.end('<html><head><meta http-equiv="refresh" content=0;url="' + url + '"></head><body></body></html>');
}
@ -3116,12 +3148,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
// Fetch the web state
parent.debug('web', 'handleRootRequestEx: success.');
var webstate = '';
var webstate = '{}';
if ((err == null) && (states != null) && (Array.isArray(states)) && (states.length == 1) && (states[0].state != null)) { webstate = obj.filterUserWebState(states[0].state); }
if ((webstate == '') && (typeof domain.defaultuserwebstate == 'object')) { webstate = JSON.stringify(domain.defaultuserwebstate); } // User has no web state, use defaults.
if ((webstate == '{}') && (typeof domain.defaultuserwebstate == 'object')) { webstate = JSON.stringify(domain.defaultuserwebstate); } // User has no web state, use defaults.
if (typeof domain.forceduserwebstate == 'object') { // Forces initial user web state if present, use it.
var webstate2 = {};
try { if (webstate != '') { webstate2 = JSON.parse(webstate); } } catch (ex) { }
try { if (webstate != '{}') { webstate2 = JSON.parse(webstate); } } catch (ex) { }
for (var i in domain.forceduserwebstate) { webstate2[i] = domain.forceduserwebstate[i]; }
webstate = JSON.stringify(webstate2);
}
@ -3152,8 +3184,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
if (obj.parent.config.settings && obj.parent.config.settings.webrtcconfig && (typeof obj.parent.config.settings.webrtcconfig == 'object')) { webRtcConfig = encodeURIComponent(JSON.stringify(obj.parent.config.settings.webrtcconfig)).replace(/'/g, '%27'); }
else if (args.webrtcconfig && (typeof args.webrtcconfig == 'object')) { webRtcConfig = encodeURIComponent(JSON.stringify(args.webrtcconfig)).replace(/'/g, '%27'); }
// Load default page style or new modern ui
var uiViewMode = 'default';
var webstateJSON = JSON.parse(webstate);
if (webstateJSON && webstateJSON.uiViewMode == 3) { uiViewMode = 'default3'; }
if (domain.sitestyle == 3) { uiViewMode = 'default3'; }
if (req.query.sitestyle == 3) { uiViewMode = 'default3'; }
// Refresh the session
render(dbGetFunc.req, dbGetFunc.res, getRenderPage(((domain.sitestyle == 3) || (req.query.sitestyle == 3) ? 'default3' : 'default'), dbGetFunc.req, domain), getRenderArgs({
render(dbGetFunc.req, dbGetFunc.res, getRenderPage(uiViewMode, dbGetFunc.req, domain), getRenderArgs({
authCookie: authCookie,
authRelayCookie: authRelayCookie,
viewmode: viewmode,
@ -3303,6 +3341,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
if (domain.scrolltotop == true) { features2 += 0x08000000; } // Show the "Scroll to top" button
if (domain.devicesearchbargroupname === true) { features2 += 0x10000000; } // Search bar will find by group name too
if (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.duo2factor != false)) && (typeof domain.duo2factor == 'object') && (typeof domain.duo2factor.integrationkey == 'string') && (typeof domain.duo2factor.secretkey == 'string') && (typeof domain.duo2factor.apihostname == 'string')) { features2 += 0x20000000; } // using Duo for 2FA is allowed
if (domain.showmodernuitoggle == true) { features2 += 0x40000000; } // Indicates that the new UI should be shown
return { features: features, features2: features2 };
}
@ -4990,7 +5029,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
ws.forwardclient = obj.tls.connect(port, node.host, tlsoptions, function () {
// The TLS connection method is the same as TCP, but located a bit differently.
parent.debug('webrelay', 'TLS connected to ' + node.host + ':' + port + '.');
parent.debug('webrelay', user.name + ' - TLS connected to ' + node.host + ':' + port + '.');
ws.forwardclient.xstate = 1;
ws._socket.resume();
});
@ -5003,7 +5042,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
ws.forwardclient.on('data', function (data) {
if (typeof data == 'string') { data = Buffer.from(data, 'binary'); }
if (obj.parent.debugLevel >= 1) { // DEBUG
parent.debug('webrelaydata', 'TCP relay data from ' + node.host + ', ' + data.length + ' bytes.');
parent.debug('webrelaydata', user.name + ' - TCP relay data from ' + node.host + ', ' + data.length + ' bytes.');
//if (obj.parent.debugLevel >= 4) { Debug(4, ' ' + Buffer.from(data, 'binary').toString('hex')); }
}
if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
@ -5018,13 +5057,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
// If the TCP connection closes, disconnect the associated web socket.
ws.forwardclient.on('close', function () {
parent.debug('webrelay', 'TCP relay disconnected from ' + node.host + ':' + port + '.');
parent.debug('webrelay', user.name + ' - TCP relay disconnected from ' + node.host + ':' + port + '.');
try { ws.close(); } catch (e) { }
});
// If the TCP connection causes an error, disconnect the associated web socket.
ws.forwardclient.on('error', function (err) {
parent.debug('webrelay', 'TCP relay error from ' + node.host + ':' + port + ': ' + err);
parent.debug('webrelay', user.name + ' - TCP relay error from ' + node.host + ':' + port + ': ' + err);
try { ws.close(); } catch (e) { }
});
@ -5035,7 +5074,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
if (node.intelamt.tls == 0) {
// A TCP connection to Intel AMT just connected, start forwarding.
ws.forwardclient.connect(port, node.host, function () {
parent.debug('webrelay', 'TCP relay connected to ' + node.host + ':' + port + '.');
parent.debug('webrelay', user.name + ' - TCP relay connected to ' + node.host + ':' + port + '.');
ws.forwardclient.xstate = 1;
ws._socket.resume();
});
@ -6502,7 +6541,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
'Referrer-Policy': 'no-referrer',
'X-XSS-Protection': '1; mode=block',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': "default-src 'none'; font-src 'self'; script-src 'self' 'unsafe-inline'" + extraScriptSrc + "; connect-src 'self'" + geourl + selfurl + "; img-src 'self' blob: data:" + geourl + " data:; style-src 'self' 'unsafe-inline'; frame-src 'self' blob: mcrouter:" + extraFrameSrc + "; media-src 'self'; form-action 'self'; manifest-src 'self'"
'Content-Security-Policy': "default-src 'none'; font-src 'self' fonts.gstatic.com data:; script-src 'self' 'unsafe-inline' " + extraScriptSrc + "; connect-src 'self'" + geourl + selfurl + "; img-src 'self' blob: data:" + geourl + " data:; style-src 'self' 'unsafe-inline' fonts.googleapis.com; frame-src 'self' blob: mcrouter:" + extraFrameSrc + "; media-src 'self'; form-action 'self'; manifest-src 'self'"
};
if (req.headers['user-agent'] && (req.headers['user-agent'].indexOf('Chrome') >= 0)) { headers['Permissions-Policy'] = 'interest-cohort=()'; } // Remove Google's FLoC Network, only send this if Chrome browser
if ((parent.config.settings.allowframing !== true) && (typeof parent.config.settings.allowframing !== 'string')) { headers['X-Frame-Options'] = 'sameorigin'; }
@ -6913,6 +6952,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
obj.app.get(url + 'auth-saml', function (req, res, next) {
var domain = getDomain(req);
if (domain.passport == null) { next(); return; }
//set RelayState when queries are passed
if (Object.keys(req.query).length != 0){
req.query.RelayState = encodeURIComponent(`${req.protocol}://${req.hostname}${req.originalUrl}`.replace('auth-saml/',''))
}
domain.passport.authenticate('saml-' + domain.id, { failureRedirect: domain.url, failureFlash: true })(req, res, next);
});
obj.app.post(url + 'auth-saml-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
@ -7049,7 +7092,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
});
} else {
// Login failed
handleRootRequestEx(req, res, domain, direct);
parent.debug('web', 'handleRootRequest: login authorization failed when returning from Duo 2FA.');
req.session.loginmode = 1;
res.redirect(domain.url + getQueryPortion(req)); // redirect back to main page
return;
}
});
}
@ -7804,7 +7850,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
async function getGroups(preset, tokenset) {
let url = '';
if (preset == 'azure') { url = strategy.groups.recursive == true ? 'https://graph.microsoft.com/v1.0/me/transitiveMemberOf' : 'https://graph.microsoft.com/v1.0/me/memberOf'; }
if (preset == 'azure') { url = strategy.groups.recursive == true ? 'https://graph.microsoft.com/v1.0/me/transitiveMemberOf?$top=999' : 'https://graph.microsoft.com/v1.0/me/memberOf?$top=999'; }
if (preset == 'google') { url = strategy.custom.customer_id ? 'https://cloudidentity.googleapis.com/v1/groups?parent=customers/' + strategy.custom.customer_id : strategy.custom.identitysource ? 'https://cloudidentity.googleapis.com/v1/groups?parent=identitysources/' + strategy.custom.identitysource : null; }
return new Promise((resolve, reject) => {
const options = {
@ -9009,7 +9055,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
}
// Filter the user web site and only output state that we need to keep
const acceptableUserWebStateStrings = ['webPageStackMenu', 'notifications', 'deviceView', 'nightMode', 'webPageFullScreen', 'search', 'showRealNames', 'sort', 'deskAspectRatio', 'viewsize', 'DeskControl', 'uiMode', 'footerBar','loctag','theme','lastThemes'];
const acceptableUserWebStateStrings = ['webPageStackMenu', 'notifications', 'deviceView', 'nightMode', 'webPageFullScreen', 'search', 'showRealNames', 'sort', 'deskAspectRatio', 'viewsize', 'DeskControl', 'uiMode', 'footerBar','loctag','theme','lastThemes','uiViewMode'];
const acceptableUserWebStateDesktopStrings = ['encoding', 'showfocus', 'showmouse', 'showcad', 'limitFrameRate', 'noMouseRotate', 'quality', 'scaling', 'agentencoding']
obj.filterUserWebState = function (state) {
if (typeof state == 'string') { try { state = JSON.parse(state); } catch (ex) { return null; } }