Added loginToken support and improved way to embed page into other sites.

This commit is contained in:
Ylian Saint-Hilaire 2017-12-13 14:52:57 -08:00
parent b171750f65
commit 9501ffd609
12 changed files with 315 additions and 211 deletions

Binary file not shown.

Binary file not shown.

2
db.js
View File

@ -80,7 +80,7 @@ module.exports.CreateDB = function (args, datapath) {
*/
}
obj.Set = function (data) { obj.file.update({ _id: data._id }, data, { upsert: true }); }
obj.Set = function (data, func) { obj.file.update({ _id: data._id }, data, { upsert: true }, func); }
obj.Get = function (id, func) { obj.file.find({ _id: id }, func); }
obj.GetAll = function (func) { obj.file.find({}, func); }
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type : 0 }, func); }

View File

@ -38,6 +38,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
obj.close = function (arg) {
if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Soft disconnect ' + obj.nodeid + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Hard disconnect ' + obj.nodeid + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
if (arg == 3) { obj.authenticated = -1; } // Don't communicate with this agent anymore, but don't disconnect (Duplicate agent).
if (obj.parent.wsagents[obj.dbNodeKey] == obj) {
delete obj.parent.wsagents[obj.dbNodeKey];
obj.parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1);
@ -293,7 +294,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (dupAgent) {
// Close the duplicate agent
obj.parent.parent.debug(1, 'Duplicate agent ' + obj.nodeid + ' (' + obj.remoteaddr + ')');
dupAgent.close();
dupAgent.close(3);
} else {
// Indicate the agent is connected
obj.parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1);

View File

@ -40,6 +40,8 @@ function CreateMeshCentralServer() {
obj.maintenanceTimer = null;
obj.serverId = null;
obj.currentVer = null;
obj.serverKey = new Buffer(obj.crypto.randomBytes(32), 'binary');
obj.loginCookieEncryptionKey = null;
try { obj.currentVer = JSON.parse(require('fs').readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
// Setup the default configuration and files paths
@ -70,7 +72,7 @@ function CreateMeshCentralServer() {
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
// Check for invalid arguments
var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug'];
var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey'];
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
@ -234,6 +236,8 @@ function CreateMeshCentralServer() {
if (obj.args.showevents) { obj.db.GetAllType('event', function (err, docs) { console.log(docs); process.exit(); }); return; }
if (obj.args.showpower) { obj.db.GetAllType('power', function (err, docs) { console.log(docs); process.exit(); }); return; }
if (obj.args.showiplocations) { obj.db.GetAllType('iploc', function (err, docs) { console.log(docs); process.exit(); }); return; }
if (obj.args.logintoken) { obj.getLoginToken(obj.args.logintoken, function (r) { console.log(r); process.exit(); }); return; }
if (obj.args.logintokenkey) { obj.showLoginTokenKey(function (r) { console.log(r); process.exit(); }); return; }
if (obj.args.dbexport) {
// Export the entire database to a JSON file
if (obj.args.dbexport == true) { obj.args.dbexport = obj.path.join(obj.datapath, 'meshcentral.db.json'); }
@ -365,13 +369,18 @@ function CreateMeshCentralServer() {
// Dispatch an event that the server is now running
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' })
obj.debug(1, 'Server started');
// Load the login cookie encryption key from the database if allowed
if ((obj.config) && (obj.config.settings) && (obj.config.settings.loginTokenOk == true)) {
obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
if ((docs.length > 0) && (docs[0].key != null)) {
obj.loginCookieEncryptionKey = Buffer.from(docs[0].key, 'hex');
} else {
obj.loginCookieEncryptionKey = obj.generateCookieKey(); obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() });
}
});
}
/*
obj.db.GetUserWithVerifiedEmail('', 'ylian.saint-hilaire@intel.com', function (err, docs) {
console.log(JSON.stringify(docs));
});
*/
obj.debug(1, 'Server started');
});
});
});
@ -823,6 +832,79 @@ function CreateMeshCentralServer() {
}
}
// Generate a time limited user login token
obj.getLoginToken = function (userid, func) {
var x = userid.split('/');
if (x == null || x.length != 3 || x[0] != 'user') { func('Invalid userid.'); return; }
obj.db.Get(userid, function (err, docs) {
if (err != null || docs == null || docs.length == 0) {
func('User ' + userid + ' not found.'); return;
} else {
// Load the login cookie encryption key from the database
obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
if ((docs.length > 0) && (docs[0].key != null)) {
// Key is present, use it.
obj.loginCookieEncryptionKey = Buffer.from(docs[0].key, 'hex');
func(obj.encodeCookie({ u: userid, a: 3 }, obj.loginCookieEncryptionKey));
} else {
// Key is not present, generate one.
obj.loginCookieEncryptionKey = obj.generateCookieKey();
obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.encodeCookie({ u: userid, a: 3 }, obj.loginCookieEncryptionKey)); });
}
});
}
});
}
// Show the yser login token generation key
obj.showLoginTokenKey = function (func) {
// Load the login cookie encryption key from the database
obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
if ((docs.length > 0) && (docs[0].key != null)) {
// Key is present, use it.
func(docs[0].key);
} else {
// Key is not present, generate one.
obj.loginCookieEncryptionKey = obj.generateCookieKey();
obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.loginCookieEncryptionKey.toString('hex')); });
}
});
}
// Generate a cryptographic key used to encode and decode cookies
obj.generateCookieKey = function () {
return new Buffer(obj.crypto.randomBytes(32), 'binary');
//return Buffer.alloc(32, 0); // Sets the key to zeros, debug only.
}
// Encode an object as a cookie using a key. (key must be 32 bytes long)
obj.encodeCookie = function (o, key) {
try {
if (key == null) { key = obj.serverKey; }
o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time
var iv = new Buffer(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key, iv);
var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]);
return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
} catch (e) { return null; }
}
// Decode a cookie back into an object using a key. Return null if it's not a valid cookie. (key must be 32 bytes long)
obj.decodeCookie = function (cookie, key, timeout) {
try {
if (key == null) { key = obj.serverKey; }
cookie = new Buffer(cookie.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64');
var decipher = obj.crypto.createDecipheriv('aes-256-gcm', key, cookie.slice(0, 12));
decipher.setAuthTag(cookie.slice(12, 16));
var o = JSON.parse(decipher.update(cookie.slice(28), 'binary', 'utf8') + decipher.final('utf8'));
if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { return null; }
o.time = o.time * 1000; // Decode the cookie creation time
o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
if (timeout == null) { timeout = 2; }
if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
return o;
} catch (e) { return null; }
}
// Debug
obj.debug = function (lvl) {
if (lvl > obj.debugLevel) return;

View File

@ -11,6 +11,7 @@ module.exports.CreateMeshMain = function (parent) {
obj.parent = parent;
obj.retry = 0;
obj.sendingMail = false;
obj.mailCookieEncryptionKey = null;
const nodemailer = require('nodemailer');
// Default account email validation mail
@ -45,7 +46,7 @@ module.exports.CreateMeshMain = function (parent) {
// Send account check mail
obj.sendAccountCheckMail = function (domain, username, email) {
if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, no reset possible.
var cookie = obj.parent.webserver.encodeCookie({ u: domain.id + '/' + username, e: email, a: 1 });
var cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 1 }, obj.mailCookieEncryptionKey);
obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountCheckSubject, domain, username, email), text: mailReplacements(accountCheckMailText, domain, username, email, cookie), html: mailReplacements(accountCheckMailHtml, domain, username, email, cookie) });
sendNextMail();
}
@ -53,7 +54,7 @@ module.exports.CreateMeshMain = function (parent) {
// Send account reset mail
obj.sendAccountResetMail = function (domain, username, email) {
if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, don't validate the email address.
var cookie = obj.parent.webserver.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 });
var cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 }, obj.mailCookieEncryptionKey);
obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountResetSubject, domain, username, email), text: mailReplacements(accountResetMailText, domain, username, email, cookie), html: mailReplacements(accountResetMailHtml, domain, username, email, cookie) });
sendNextMail();
}
@ -74,7 +75,7 @@ module.exports.CreateMeshMain = function (parent) {
sendNextMail(); // Send the next mail
} else {
obj.retry++;
console.log('SMTP server failed: ' + err.response);
console.log('SMTP server failed: ' + JSON.stringify(err));
if (obj.retry < 6) { setTimeout(sendNextMail, 60000); } // Wait and try again
}
});
@ -86,10 +87,22 @@ module.exports.CreateMeshMain = function (parent) {
if (err == null) {
console.log('SMTP mail server ' + parent.config.smtp.host + ' working as expected.');
} else {
console.log('SMTP mail server ' + parent.config.smtp.host + ' failed: ' + err.response);
console.log('SMTP mail server ' + parent.config.smtp.host + ' failed: ' + JSON.stringify(err));
}
});
}
// Load the cookie encryption key from the database
obj.parent.db.Get('MailCookieEncryptionKey', function (err, docs) {
if ((docs.length > 0) && (docs[0].key != null)) {
// Key is present, use it.
obj.mailCookieEncryptionKey = Buffer.from(docs[0].key, 'hex');
} else {
// Key is not present, generate one.
obj.mailCookieEncryptionKey = obj.parent.generateCookieKey();
obj.parent.db.Set({ _id: 'MailCookieEncryptionKey', key: obj.mailCookieEncryptionKey.toString('hex'), time: Date.now() });
}
});
return obj;
}

View File

@ -84,7 +84,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
}
} else {
// Get the session from the cookie
var cookie = obj.parent.parent.webserver.decodeCookie(req.query.auth);
var cookie = obj.parent.parent.decodeCookie(req.query.auth);
if (cookie != null) {
obj.authenticated = true;
if (cookie.tcpport != null) {

View File

@ -833,7 +833,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
case 'agentdisconnect':
{
// Force mesh agent disconnection
forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
obj.parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
break;
}
case 'close':
@ -855,7 +855,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (command.nodeid) { cookieContent.nodeid = command.nodeid; }
if (command.tcpaddr) { cookieContent.tcpaddr = command.tcpaddr; } // Indicates the browser want to agent to TCP connect to a remote address
if (command.tcpport) { cookieContent.tcpport = command.tcpport; } // Indicates the browser want to agent to TCP connect to a remote port
command.cookie = obj.parent.encodeCookie(cookieContent);
command.cookie = obj.parent.parent.encodeCookie(cookieContent);
ws.send(JSON.stringify(command));
}
}

View File

@ -561,7 +561,7 @@ module.exports.CreateMultiServer = function (parent, args) {
if (path.substring(path.length - 11) == '/.websocket') { path = path.substring(0, path.length - 11); }
var queryStr = ''
for (var i in req.query) { queryStr += ((queryStr == '') ? '?' : '&') + i + '=' + req.query[i]; }
if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); }
if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.parent.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); }
var url = obj.peerConfig.servers[serverid].url + path + queryStr;
// Setup an connect the web socket

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.1.0-o",
"version": "0.1.0-x",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -56,41 +56,43 @@
<div style="float:right">
<div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="display:none;min-width:28px;font-size:20px;border-radius:5px;background-color:lightblue;text-align:center;margin:8px;cursor:pointer;padding:4px" title="Click to view current notifications">0</div>
</div>
<p>{{{logoutControl}}}</p>
<p id="logoutControl">{{{logoutControl}}}</p>
</div>
<div id=topbar class=noselect style=display:none>
<div>
<div id=topbarmaster>
<div id=topbar class=noselect style=display:none>
<div>
<table style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
<tr>
<td id=MainMenuMyDevices style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(1)>My Devices</td>
<td id=MainMenuMyAccount style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(2)>My Account</td>
<td id=MainMenuMyEvents style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(3)>My Events</td>
<td id=MainMenuMyFiles style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(5)>My Files</td>
<td id=MainMenuMyUsers style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(4)>My Users</td>
<td class=style3 style=height:24px>&nbsp;</td>
</tr>
</table>
<div id="MainSubMenuSpan" style=display:none>
<table id="MainSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
<div>
<table style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
<tr>
<td id=MainDev style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(10)>General</td>
<td id=MainDevDesktop style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(11)>Desktop</td>
<td id=MainDevTerminal style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(12)>Terminal</td>
<td id=MainDevFiles style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(13)>Files</td>
<td id=MainDevAmt style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(14)>Intel&reg; AMT</td>
<td id=MainDevConsole style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(15)>Console</td>
<td class=style3 style=height:24px>&nbsp;</td>
</tr>
</table>
</div>
<div id="MeshSubMenuSpan" style=display:none>
<table id="MeshSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
<tr>
<td id=MeshGeneral style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(20)>General</td>
<td id=MainMenuMyDevices style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(1)>My Devices</td>
<td id=MainMenuMyAccount style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(2)>My Account</td>
<td id=MainMenuMyEvents style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(3)>My Events</td>
<td id=MainMenuMyFiles style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(5)>My Files</td>
<td id=MainMenuMyUsers style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(4)>My Users</td>
<td class=style3 style=height:24px>&nbsp;</td>
</tr>
</table>
<div id="MainSubMenuSpan" style=display:none>
<table id="MainSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
<tr>
<td id=MainDev style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(10)>General</td>
<td id=MainDevDesktop style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(11)>Desktop</td>
<td id=MainDevTerminal style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(12)>Terminal</td>
<td id=MainDevFiles style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(13)>Files</td>
<td id=MainDevAmt style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(14)>Intel&reg; AMT</td>
<td id=MainDevConsole style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(15)>Console</td>
<td class=style3 style=height:24px>&nbsp;</td>
</tr>
</table>
</div>
<div id="MeshSubMenuSpan" style=display:none>
<table id="MeshSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
<tr>
<td id=MeshGeneral style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(20)>General</td>
<td class=style3 style=height:24px>&nbsp;</td>
</tr>
</table>
</div>
</div>
</div>
</div>
@ -265,7 +267,9 @@
<table style="width:100%" cellpadding="0" cellspacing="0">
<tr>
<td style=width:auto valign=top>
<h1><span id=p10deviceName></span> - General</h1>
<div id="p10title">
<h1><span id=p10deviceName></span> - General</h1>
</div>
<div id=p10html></div>
</td>
<td style=width:20px></td>
@ -279,7 +283,9 @@
<div id=p10html3></div>
</div>
<div id=p11 style=display:none>
<h1 id=p11deviceNameHeader><span id=p11deviceName></span> - Desktop</h1>
<div id="p11title">
<h1 id=p11deviceNameHeader><span id=p11deviceName></span> - Desktop</h1>
</div>
<div id="p14warning" style='max-width:100%;display:none;cursor:pointer;margin-bottom:5px' onclick="showFeaturesDlg()">
<div class=icon2 style="float:left;margin:7px"></div>
<div style='width:auto;border-radius:8px;padding:8px;background-color:lightsalmon'>Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p14warninga">, click here to enable it.</span></div>
@ -334,7 +340,7 @@
</table>
</div>
<div id=p12 style=display:none>
<h1><span id=p12deviceName></span> - Terminal</h1>
<div id="p12title"><h1><span id=p12deviceName></span> - Terminal</h1></div>
<div id="p12warning" style='max-width:100%;display:none;cursor:pointer;margin-bottom:5px' onclick=showFeaturesDlg()>
<div class="icon2" style="float:left;margin:7px"></div>
<div style='width:auto;border-radius:8px;padding:8px;background-color:lightsalmon'>Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p14warninga">, click here to enable it.</span></div>
@ -389,7 +395,7 @@
</table>
</div>
<div id=p13 style=display:none>
<h1><span id=p13deviceName></span> - Files</h1>
<div id="p13title"><h1><span id=p13deviceName></span> - Files</h1></div>
<table id="p13toolbar" style="width: 100%" cellpadding="0" cellspacing="0">
<tr>
<td style="background-color:#C0C0C0;border-bottom:2px solid black;padding:2px">
@ -443,11 +449,11 @@
</table>
</div>
<div id=p14 style=display:none>
<h1><span id=p14deviceName></span> - Intel&reg; AMT</h1>
<div id="p14title"><h1><span id=p14deviceName></span> - Intel&reg; AMT</h1></div>
<iframe id=p14iframe style="width:100%;height:650px;border:0;overflow:hidden" src="/commander.htm"></iframe>
</div>
<div id=p15 style=display:none>
<h1><span id=p15deviceName></span> - Console</h1>
<div id="p15title"><h1><span id=p15deviceName></span> - Console</h1></div>
<table cellpadding=0 cellspacing=0 style="width:100%;padding:0px;padding:0px;margin-top:0px">
<tr>
<td style=background:#C0C0C0>
@ -494,7 +500,7 @@
<tr>
<td style="text-align:left"></td>
<td style="text-align:right">
<a id="verifyEmailId2" style="color:yellow;margin-left:3px;cursor:pointer" onclick="account_showVerifyEmail()">Verify Email</a>
<a id="verifyEmailId2" style="color:yellow;margin-left:3px;cursor:pointer;display:none" onclick="account_showVerifyEmail()">Verify Email</a>
<a style="margin-left:3px" href="terms">Terms &amp; Privacy</a>
</td>
</tr>
@ -599,6 +605,7 @@
</div>
</div>
<script type="text/javascript">
var args;
var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
var StatusStrs = ['Disconnected', 'Connecting...', 'Setup...', 'Connected', 'Intel&reg; AMT Connected'];
var sort = 0;
@ -635,13 +642,25 @@
if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
// Check if we are in debug mode
var args = parseUriArgs();
args = parseUriArgs();
debugmode = (args.debug == 1);
QV('p13AutoConnect', debugmode); // Files
QV('autoconnectbutton2', debugmode); // Terminal
QV('autoconnectbutton1', debugmode); // Desktop
// Setup page visuals
if (args.hide) {
var hide = parseInt(args.hide);
QV('masthead', !(hide & 1));
QV('topbarmaster', !(hide & 2));
QV('footer', !(hide & 4));
QV('p10title', !(hide & 8));
QV('p11title', !(hide & 8));
QV('p12title', !(hide & 8));
QV('p13title', !(hide & 8));
QV('p14title', !(hide & 8));
QV('p15title', !(hide & 8));
}
p1updateInfo();
// Setup the context menu
@ -733,6 +752,8 @@
powerTimelineUpdate = null;
deleteAllNotifications(); // Close and clear notifications if present
hideContextMenu(); // Hide the context menu if present
QV('verifyEmailId2', false);
QV('logoutControl', false);
} else if (state == 2) {
// Fetch list of meshes, nodes, files
meshserver.Send({ action: 'meshes' });
@ -850,7 +871,7 @@
}
case 'msg': {
// Check if this is a message from a node
if (message.nodeid != undefined) {
if (message.nodeid != null) {
var index = -1;
for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } }
if (index != -1) {
@ -858,15 +879,15 @@
if (message.type == 'console') { p15consoleReceive(nodes[index], message.value); } // This is a console message.
if (message.type == 'notify') { // This is a notification message.
var n = { text:message.value };
if (message.nodeid != undefined) { n.nodeid = message.nodeid; }
if (message.tag != undefined) { n.tag = message.tag; }
if (message.nodeid != null) { n.nodeid = message.nodeid; }
if (message.tag != null) { n.tag = message.tag; }
addNotification(n);
}
}
} else {
if (message.type == 'notify') { // This is a notification message.
var n = { text:message.value };
if (message.tag != undefined) { n.tag = message.tag; }
if (message.tag != null) { n.tag = message.tag; }
addNotification(n);
}
}
@ -956,7 +977,7 @@
}
case 'createmesh': {
// A new mesh was created
if (message.event.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()] != undefined) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
if (message.event.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
updateMeshes();
updateDevices();
@ -966,7 +987,7 @@
}
case 'meshchange': {
// Update mesh information
if (meshes[message.event.meshid] == undefined) {
if (meshes[message.event.meshid] == null) {
// This is a new mesh for us
meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
meshserver.Send({ action: 'nodes' }); // Request a refresh of all nodes (TODO: We could optimize this to only request nodes for the new mesh).
@ -977,7 +998,7 @@
meshes[message.event.meshid].links = message.event.links;
// Check if we lost rights to this mesh in this change.
if (meshes[message.event.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()] == undefined) {
if (meshes[message.event.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()] == null) {
if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
delete meshes[message.event.meshid];
@ -1065,21 +1086,21 @@
node.wifiloc = message.event.node.wifiloc;
node.gpsloc = message.event.node.gpsloc;
node.userloc = message.event.node.userloc;
if (message.event.node.agent != undefined) {
if (node.agent == undefined) node.agent = {};
if (message.event.node.agent.ver != undefined) { node.agent.ver = message.event.node.agent.ver; }
if (message.event.node.agent.id != undefined) { node.agent.id = message.event.node.agent.id; }
if (message.event.node.agent.caps != undefined) { node.agent.caps = message.event.node.agent.caps; }
if (message.event.node.agent.core != undefined) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
if (message.event.node.agent != null) {
if (node.agent == null) node.agent = {};
if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; }
if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; }
if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; }
if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
node.agent.tag = message.event.node.agent.tag;
}
if (message.event.node.intelamt != undefined) {
if (node.intelamt == undefined) node.intelamt = {};
if (message.event.node.intelamt.host != undefined) { node.intelamt.user = message.event.node.intelamt.host; }
if (message.event.node.intelamt.user != undefined) { node.intelamt.user = message.event.node.intelamt.user; }
if (message.event.node.intelamt.tls != undefined) { node.intelamt.tls = message.event.node.intelamt.tls; }
if (message.event.node.intelamt.ver != undefined) { node.intelamt.ver = message.event.node.intelamt.ver; }
if (message.event.node.intelamt.state != undefined) { node.intelamt.state = message.event.node.intelamt.state; }
if (message.event.node.intelamt != null) {
if (node.intelamt == null) node.intelamt = {};
if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; }
if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; }
if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; }
if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; }
if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
}
node.namel = node.name.toLowerCase();
if (node.host) { node.hostl = node.host.toLowerCase(); } else { node.hostl = node.namel; }
@ -1134,7 +1155,7 @@
}
case 'scanamtdevice': {
// Populate the Intel AMT scan dialog box with the result of the RMCP scan
if ((xxdialogMode == undefined) || (!Q('dp1range')) || (Q('dp1range').value != message.event.range)) return;
if ((xxdialogMode == null) || (!Q('dp1range')) || (Q('dp1range').value != message.event.range)) return;
var x = '';
if (message.event.results == null) {
// The scan could not occur because of an error. Likely the user range was invalid.
@ -1158,6 +1179,12 @@
QE('dp1rangebutton', true);
break;
}
case 'notify': {
var n = { text: message.event.value };
if (message.event.tag != null) { n.tag = message.event.tag; }
addNotification(n);
break;
}
}
break;
}
@ -1275,7 +1302,7 @@
for (var i in nodes) {
if (nodes[i].v == false) continue;
var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
if (meshlinks == undefined) continue;
if (meshlinks == null) continue;
if (sort == 0) {
// Mesh header
if (nodes[i].meshid != current) {
@ -1312,9 +1339,9 @@
var title = EscapeHtml(nodes[i].name);
if (title.length == 0) { title = '<i>None</i>'; }
if ((nodes[i].host != undefined) && (nodes[i].host.length > 0)) { title += " / " + EscapeHtml(nodes[i].host); }
if ((nodes[i].host != null) && (nodes[i].host.length > 0)) { title += " / " + EscapeHtml(nodes[i].host); }
var name = EscapeHtml(nodes[i].name);
if (showHostnames == true && nodes[i].host != undefined) name = EscapeHtml(nodes[i].host);
if (showHostnames == true && nodes[i].host != null) name = EscapeHtml(nodes[i].host);
if (name.length == 0) { name = '<i>None</i>'; }
// Node
@ -1336,9 +1363,9 @@
if (sort == 0 && Q('SearchInput').value == '') {
for (var i in meshes) {
var mesh = meshes[i], meshlink = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
if (meshlink != undefined) {
if (meshlink != null) {
var meshrights = meshlink.rights;
if (displayedMeshes[mesh._id] == undefined) {
if (displayedMeshes[mesh._id] == null) {
if (current != '') { r += '</tr></table>'; }
r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt><span style=float:right>';
r += getMeshActions(mesh, meshrights);
@ -1575,7 +1602,7 @@
if ((node.conn & 4) != 0) states.push('<span title="Intel&reg; AMT is routable.">Intel&reg; AMT</span>');
if ((node.conn & 8) != 0) states.push('<span title="Mesh agent is reachable using another agent as relay.">Relay</span>');
}
if ((node.pwr != undefined) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); }
if ((node.pwr != null) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); }
return states.join(', ');
}
@ -1660,7 +1687,7 @@
function powerSort(a, b) { var ap = a.pwr?a.pwr:0; var bp = b.pwr?b.pwr:0; if (ap == bp) { if (showHostnames == true) { if (a.hostl > b.hostl) return 1; if (a.hostl < b.hostl) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } if (ap > bp) return 1; if (ap < bp) return -1; return 0; }
function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; }
function deviceHostSort(a, b) { if (a.hostl > b.hostl) return 1; if (a.hostl < b.hostl) return -1; return 0; }
function onSearchInputChanged() { var x = Q('SearchInput').value.toLowerCase(); putstore("search", x); if (x == '') { for (var d in nodes) { nodes[d].v = true; } } else { for (var d in nodes) { nodes[d].v = (nodes[d].name.toLowerCase().indexOf(x) >= 0) || (nodes[d].hostl != undefined && nodes[d].hostl.toLowerCase().indexOf(x) >= 0); } } updateDevices(); }
function onSearchInputChanged() { var x = Q('SearchInput').value.toLowerCase(); putstore("search", x); if (x == '') { for (var d in nodes) { nodes[d].v = true; } } else { for (var d in nodes) { nodes[d].v = (nodes[d].name.toLowerCase().indexOf(x) >= 0) || (nodes[d].hostl != null && nodes[d].hostl.toLowerCase().indexOf(x) >= 0); } } updateDevices(); }
function onSearchFocus(x) { searchFocus = x; }
function onMapSearchFocus(x) { mapSearchFocus = x; }
function onConsoleFocus(x) { consoleFocus = x; }
@ -1668,8 +1695,8 @@
var contextelement = null;
function handleContextMenu(event) {
hideContextMenu();
var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
var scrollLeft = (window.pageXOffset !== null) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var scrollTop = (window.pageYOffset !== null) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
var elem = document.elementFromPoint(event.pageX - scrollLeft, event.pageY - scrollTop);
if (elem && elem != null && elem.id == "MxMESH") {
contextelement = elem;
@ -1743,7 +1770,7 @@
for (var i in nodes) {
var loc = map_parseNodeLoc(nodes[i]);
var feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == undefined))) { // Draw markers for devices with locations
if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
lat = loc[0];
lon = loc[1];
var type = loc[2];
@ -1848,7 +1875,7 @@
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
if (feature) {
var nodeid = feature.getId();
if (nodeid != undefined) { gotoDevice(nodeid, 10); } // Goto general info tab
if (nodeid != null) { gotoDevice(nodeid, 10); } // Goto general info tab
else { // For pointer
var nodeFeatgoto = getCorrespondingFeature(feature); gotoDevice(nodeFeatgoto.getId(), 10);
}
@ -2149,7 +2176,7 @@
function updatePlaceNodeTable(inputSearch) {
var elements = document.getElementsByName("PlaceMapDeviceCheckbox"), count = 0;
for (var i in nodes) {
var visible = ((nodes[i].namel.indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != undefined && nodes[i].hostl.indexOf(inputSearch) >= 0));
var visible = ((nodes[i].namel.indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != null && nodes[i].hostl.indexOf(inputSearch) >= 0));
if (visible) { count++; }
QV(nodes[i]._id + '-rowid', visible);
}
@ -2157,7 +2184,7 @@
/*
console.log(selected);
for (var i in nodes) {
if ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != undefined && nodes[i].hostl.toLowerCase().indexOf(inputSearch) >= 0)) {
if ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != null && nodes[i].hostl.toLowerCase().indexOf(inputSearch) >= 0)) {
console.log(selected.indexOf(nodes[i]._id));
x += '<div class=noselect id=' + nodes[i]._id + '-rowid onclick=selectNodeToPlace(event,\''+ nodes[i]._id +'\') style=background-color:lightgray;margin-bottom:4px;border-radius:2px><input name=PlaceMapDeviceCheckbox id=' + nodes[i]._id + '-checkid type=checkbox style=width:16px;display:inline ' + ((selected.indexOf(nodes[i]._id) >= 0)?'checked':'') + ' />';
x += '<div class=j' + nodes[i].icon + ' style=width:16px;height:16px;margin-top:2px;margin-right:4px;display:inline-block></div><div style=width:16px;display:inline>' + nodes[i].name + '</div></div>';
@ -2384,7 +2411,7 @@
// Attribute: Mesh Agent
var agentsStr = ['Unknown', 'Windows 32bit console', 'Windows 64bit console', 'Windows 32bit service', 'Windows 64bit service', 'Linux 32bit', 'Linux 64bit', 'MIPS', 'XENx86', 'Android ARM', 'Linux ARM', 'OSX 32bit', 'Android x86', 'PogoPlug ARM', 'Android APK', 'Linux Poky x86-32bit', 'OSX 64bit', 'ChromeOS', 'Linux Poky x86-64bit', 'Linux NoKVM x86-32bit', 'Linux NoKVM x86-64bit', 'Windows MinCore console', 'Windows MinCore service', 'NodeJS', 'ARM-Linaro', 'ARMv6l / ARMv7l' ];
if ((node.agent != undefined) && (node.agent.id != undefined) && (node.agent.ver != undefined)) {
if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) {
var str = '';
if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; }
if (node.agent.ver != 0) { str += ' v' + node.agent.ver; }
@ -2392,12 +2419,12 @@
}
// Attribute: Intel AMT
if (node.intelamt != undefined) {
if (node.intelamt != null) {
var str = '';
var provisioningStates = { 0: 'Not Activated (Pre)', 1: 'Not Activated (In)', 2: 'Activated' };
if (node.intelamt.ver == undefined || node.intelamt.state == undefined) { str += '<i>Unknown Version & State</i>'; } else { str += (provisioningStates[node.intelamt.state] + ', v' + node.intelamt.ver); }
if (node.intelamt.ver == null || node.intelamt.state == null) { str += '<i>Unknown Version & State</i>'; } else { str += (provisioningStates[node.intelamt.state] + ', v' + node.intelamt.ver); }
if (node.intelamt.tls == 1) { str += ', TLS'; }
if (node.intelamt.user == undefined || node.intelamt.user == '') {
if (node.intelamt.user == null || node.intelamt.user == '') {
if ((meshrights & 4) != 0) {
str += ', <i style=color:#FF0000;cursor:pointer title="Edit Intel&reg; AMT credentials" onclick=editDeviceAmtSettings("' + node._id + '")>No Credentials</i>';
} else {
@ -2412,7 +2439,7 @@
}
// Attribute: Mesh Agent Tag
if ((node.agent != undefined) && (node.agent.tag != undefined)) {
if ((node.agent != null) && (node.agent.tag != null)) {
x += addDeviceAttribute('Agent Tag', node.agent.tag);
}
@ -2481,13 +2508,13 @@
// Show or hide the tabs
// mesh.mtype: 1 = Intel AMT only, 2 = Mesh Agent
// node.agent.caps (bitmask): 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console
QV('MainDevDesktop', (mesh.mtype == 1) || (node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 1) != 0));
QV('MainDevTerminal', (mesh.mtype == 1) || (node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 2) != 0));
QV('MainDevFiles', (mesh.mtype == 2) && ((node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 4) != 0)));
QV('MainDevAmt', node.intelamt != undefined);
QV('MainDevConsole', consoleRights && (mesh.mtype == 2) && ((node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 8) != 0)));
QV('p15uploadCore', (node.agent != undefined) && (node.agent.caps != undefined) && ((node.agent.caps & 16) != 0));
QH('p15coreName', ((node.agent != undefined) && (node.agent.core != undefined))?node.agent.core:'');
QV('MainDevDesktop', (mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0));
QV('MainDevTerminal', (mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0));
QV('MainDevFiles', (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0)));
QV('MainDevAmt', node.intelamt != null);
QV('MainDevConsole', consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0)));
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
// Setup/Refresh Intel AMT tab
var amtFrameNode = Q('p14iframe').contentWindow.getCurrentMeshNode();
@ -2534,7 +2561,7 @@
// Called when MeshCommander needs new credentials or updated credentials.
function updateAmtCredentials(forceDialog) {
var node = getNodeFromId(desktopNode._id);
if ((forceDialog == true) || (node.intelamt.user == undefined) || (node.intelamt.user == '')) {
if ((forceDialog == true) || (node.intelamt.user == null) || (node.intelamt.user == '')) {
editDeviceAmtSettings(desktopNode._id, updateAmtCredentialsEx);
} else {
Q('p14iframe').contentWindow.connectButtonfunctionEx();
@ -2619,9 +2646,9 @@
x += addHtmlValue('Username', '<input id=dp10username style=width:230px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
x += addHtmlValue('Password', '<input id=dp10password type=password style=width:230px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
x += addHtmlValue('Security', '<select id=dp10tls style=width:236px><option value=0>No TLS security</option><option value=1>TLS security required</option></select>');
if ((node.intelamt.user != undefined) && (node.intelamt.user != '')) { buttons = 7; }
if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; }
setDialogMode(2, "Edit Intel&reg; AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func });
if ((node.intelamt.user != undefined) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
if ((node.intelamt.user != null) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
Q('dp10tls').value = node.intelamt.tls;
validateDeviceAmtSettings();
}
@ -2783,7 +2810,7 @@
var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:230px maxlength=32 onchange=p10editdevicevalueValidate(' + mode + ') onkeyup=p10editdevicevalueValidate(' + mode + ') />');
setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode);
var v = currentNode[showEditNodeValueDialog_modes2[mode]];
if (v == undefined) v = '';
if (v == null) v = '';
Q('dp10devicevalue').value = v;
p10editdevicevalueValidate();
}
@ -2805,7 +2832,7 @@
var desktopNode;
function setupDesktop() {
// Setup the remote desktop
if ((desktopNode != currentNode) && (desktop != undefined)) { desktop.Stop(); delete desktop; desktop = undefined; }
if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); delete desktop; desktop = null; }
desktopNode = currentNode;
updateDesktopButtons();
@ -2816,15 +2843,15 @@
// Show and enable the right buttons
function updateDesktopButtons() {
var mesh = meshes[desktopNode.meshid];
var deskState = ((desktop != undefined) && (desktop.state != 0));
var deskState = ((desktop != null) && (desktop.state != 0));
// Show the right buttons
QV('disconnectbutton1span', (deskState == true));
QV('connectbutton1span', (deskState == false) && (mesh.mtype == 2));
QV('connectbutton1hspan', (deskState == false) && (desktopNode.intelamt != undefined && ((desktopNode.intelamt.ver != undefined) || (mesh.mtype == 1))));
QV('connectbutton1hspan', (deskState == false) && (desktopNode.intelamt != null && ((desktopNode.intelamt.ver != null) || (mesh.mtype == 1))));
// Show the right settings
QV('d7amtkvm', (desktopNode.intelamt != undefined && ((desktopNode.intelamt.ver != undefined) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
QV('d7amtkvm', (desktopNode.intelamt != null && ((desktopNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
QV('d7meshkvm', (mesh.mtype == 2) && ((deskState == false) || (desktop.contype == 1)));
// Enable buttons
@ -2839,10 +2866,10 @@
function autoConnectDesktop(e) { if (autoConnectDesktopTimer == null) { autoConnectDesktopTimer = setInterval(connectDesktop, 100); } else { clearInterval(autoConnectDesktopTimer); autoConnectDesktopTimer = null; } }
function connectDesktop(e, contype) {
if (desktop == undefined) {
if (desktop == null) {
if (contype == 2) {
// Setup the Intel AMT remote desktop
if ((desktopNode.intelamt.user == undefined) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'));
desktop.onStateChanged = onDesktopStateChange;
desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
@ -2865,7 +2892,7 @@
// Disconnect and clean up the remote desktop
desktop.Stop();
delete desktop;
desktop = undefined;
desktop = null;
}
}
@ -2874,14 +2901,14 @@
if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
QH('deskstatus', StatusStrs[xstate]);
QE('deskSaveBtn', state == 3);
QV('deskFocusBtn', (desktop != undefined) && (desktop.contype == 2) && (state != 0) && (desktopsettings.showfocus));
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (state != 0) && (desktopsettings.showfocus));
QE('DeskCAD', state == 3);
switch (state) {
case 0:
// Disconnect and clean up the remote desktop
desktop.Stop();
delete desktop;
desktop = undefined;
desktop = null;
QV('DeskFocus', false);
deskFocusBtn.value = 'All Focus';
if (fullscreen == true) { deskToggleFull(); }
@ -2923,7 +2950,7 @@
d7showcursor.checked = desktopsettings.showmouse;
d7bitmapquality.value = desktopsettings.quality;
d7bitmapscaling.value = desktopsettings.scaling;
QV('deskFocusBtn', (desktop != undefined) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
}
var fullscreen = false;
@ -2988,22 +3015,22 @@
// Send CTRL-ALT-DEL
function sendCAD() {
if (xxdialogMode || desktop == undefined || desktop.State != 3) return;
if (xxdialogMode || desktop == null || desktop.State != 3) return;
desktop.m.sendcad();
}
// Save the desktop image to file
function deskSaveImage() {
if (xxdialogMode || desktop == undefined || desktop.State != 3) return;
if (xxdialogMode || desktop == null || desktop.State != 3) return;
var d = new Date(), n = 'Desktop-' + currentNode.name + '-' + d.getFullYear() + "-" + ("0" + (d.getMonth() + 1)).slice(-2) + "-" + ("0" + d.getDate()).slice(-2) + "-" + ("0" + d.getHours()).slice(-2) + "-" + ("0" + d.getMinutes()).slice(-2);
Q("Desk")['toBlob'](function (blob) { saveAs(blob, n + ".jpg"); });
}
function dmousedown(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mousedown(e) }
function dmouseup(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mouseup(e) }
function dmousemove(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mousemove(e) }
function dmousewheel(e) { if (!xxdialogMode && desktop != undefined) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
function drotate(x) { if (!xxdialogMode && desktop != undefined) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
function dmousedown(e) { if (!xxdialogMode && desktop != null) desktop.m.mousedown(e) }
function dmouseup(e) { if (!xxdialogMode && desktop != null) desktop.m.mouseup(e) }
function dmousemove(e) { if (!xxdialogMode && desktop != null) desktop.m.mousemove(e) }
function dmousewheel(e) { if (!xxdialogMode && desktop != null) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
//
// TERMINAL
@ -3012,7 +3039,7 @@
var terminalNode;
function setupTerminal() {
// Setup the terminal
if ((terminalNode != currentNode) && (terminal != undefined)) { terminal.Stop(); delete terminal; terminal = undefined; }
if ((terminalNode != currentNode) && (terminal != null)) { terminal.Stop(); delete terminal; terminal = null; }
terminalNode = currentNode;
updateTerminalButtons();
}
@ -3020,12 +3047,12 @@
// Show and enable the right buttons
function updateTerminalButtons() {
var mesh = meshes[terminalNode.meshid];
var termState = ((terminal != undefined) && (terminal.state != 0));
var termState = ((terminal != null) && (terminal.state != 0));
// Show the right buttons
QV('disconnectbutton2span', (termState == true));
QV('connectbutton2span', (termState == false) && (mesh.mtype == 2));
QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != undefined && ((terminalNode.intelamt.ver != undefined) || (mesh.mtype == 1))));
QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != null && ((terminalNode.intelamt.ver != null) || (mesh.mtype == 1))));
// Enable buttons
var online = ((terminalNode.conn & 1) != 0); // If Agent (1) connected, enable Terminal
@ -3052,10 +3079,10 @@
// Disconnected, clear the terminal
xterminal.m.TermResetScreen();
xterminal.m.TermDraw();
if (terminal != undefined) {
if (terminal != null) {
terminal.Stop();
delete terminal;
terminal = undefined;
terminal = null;
}
break;
case 3:
@ -3072,7 +3099,7 @@
if (!terminal) {
if (contype == 2) {
// Setup the Intel AMT terminal
if ((terminalNode.intelamt.user == undefined) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal); return; }
if ((terminalNode.intelamt.user == null) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal); return; }
terminal = CreateAmtRedirect(CreateAmtRemoteTerminal('Term'));
terminal.onStateChanged = onTerminalStateChange;
terminal.Start(terminalNode._id, 16994, '*', '*', 0);
@ -3089,7 +3116,7 @@
//QH('Term', '');
terminal.Stop();
delete terminal;
terminal = undefined;
terminal = null;
}
Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses.
}
@ -3146,7 +3173,7 @@
filesNode = currentNode;
var online = ((filesNode.conn & 1) != 0)?true:false; // If Agent (1) connected, enable Terminal
QE('p13Connect', online);
if (((samenode == false) || (online == false)) && files) { files.Stop(); delete files; files = undefined; }
if (((samenode == false) || (online == false)) && files) { files.Stop(); delete files; files = null; }
}
function onFilesStateChange(xfiles, state) {
@ -3161,7 +3188,7 @@
QH('p13currentpath', '');
QE('p13FolderUp', false);
p13setActions();
if (files != undefined) { files.Stop(); delete files; files = undefined; }
if (files != null) { files.Stop(); delete files; files = null; }
break;
case 3:
files.Send(JSON.stringify({ action: 'ls', reqid: 1, path: '' }));
@ -3192,7 +3219,7 @@
//QH('Term', '');
files.Stop();
delete files;
files = undefined;
files = null;
}
}
@ -3249,11 +3276,11 @@
// Figure out the date
var fdatestr = '';
if (f.d != undefined) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
if (f.d != null) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
// Figure out the size
var fsize = '';
if (f.s != undefined) { fsize = getFileSizeStr(f.s); }
if (f.s != null) { fsize = getFileSizeStr(f.s); }
var h = '';
if (f.t < 3) {
@ -3274,7 +3301,7 @@
QE('p13FolderUp', p13filetreelocation.length != 0);
// Re-check all boxes if needed using names
if (checkedNames != undefined) {
if (checkedNames != null) {
checkedBoxes = [];
checkboxes = document.getElementsByName('fd');
for (var i in filetreexx) { if (checkedNames.indexOf(filetreexx[i].n) >= 0) { checkedBoxes.push(filetreexx[i].nx); } }
@ -3295,7 +3322,7 @@
}
function p13folderup(x) {
if (x == undefined) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
if (x == null) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
files.Send(JSON.stringify({ action: 'ls', reqid: 1, path: p13filetreelocation.join('/') }));
}
@ -3306,7 +3333,7 @@
function p13sort_files(files) {
var r = [], sortselection = Q('p13sortdropdown').value;
for (var i in files) { files[i].nx = i; if (files[i].s == undefined) { files[i].s = 0; } if (files[i].n == undefined) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
for (var i in files) { files[i].nx = i; if (files[i].s == null) { files[i].s = 0; } if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
p13sortorder = 1;
if (sortselection > 3) { p13sortorder = -1; sortselection -= 3; }
if (sortselection == 1) { r.sort(p13sort_filename); }
@ -3402,7 +3429,7 @@
//console.log('p13downloadFileCancel');
downloadFile.Stop();
delete downloadFile;
downloadFile = undefined;
downloadFile = null;
}
// Called by the file transport to indicate when the transport connection state has changed
@ -3410,7 +3437,7 @@
switch (state) {
case 0: // Transport as disconnected. If this is not part of an abort, we need to save the file
setDialogMode(0); // Close any dialog boxes if present
if ((downloadFile != undefined) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file
if ((downloadFile != null) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file
break;
case 3: // Transport as connected, send a command to indicate we want to start a file download
downloadFile.Send(JSON.stringify({ action: 'download', reqid: 1, path: downloadFile.xpath }));
@ -3492,12 +3519,12 @@
// Used to cancel the entire transfer.
function p13uploadFileCancel(button, tag) {
if (uploadFile != undefined) {
if (uploadFile.ws != undefined) {
if (uploadFile != null) {
if (uploadFile.ws != null) {
uploadFile.ws.Stop();
uploadFile.ws = undefined;
uploadFile.ws = null;
}
uploadFile = undefined;
uploadFile = null;
}
setDialogMode(0); // Close any dialog boxes if present
}
@ -3505,7 +3532,7 @@
// Receive upload ack from the mesh agent, use this to keep sending more data
function p13gotUploadData(data) {
var cmd = JSON.parse(data);
if ((uploadFile == undefined) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
if (cmd.action == 'uploadstart') {
p13uploadNextPart(false);
@ -3524,7 +3551,7 @@
var end = uploadFile.xptr + 4096;
if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
if (start == data.byteLength) {
if (uploadFile.ws != undefined) { uploadFile.ws.Stop(); uploadFile.ws = undefined; }
if (uploadFile.ws != null) { uploadFile.ws.Stop(); uploadFile.ws = null; }
if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadReconnect(); } else { p13uploadFileCancel(); }
} else {
var datapart = data.slice(start, end);
@ -3567,7 +3594,7 @@
var mesh = meshes[consoleNode.meshid];
var meshrights = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
if ((meshrights & 16) != 0) {
if (consoleNode.consoleText == undefined) { consoleNode.consoleText = ''; }
if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
if (samenode == false) {
QH('p15agentConsole', consoleNode.consoleText);
Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
@ -3618,7 +3645,7 @@
// Handle Mesh Agent console data
function p15consoleReceive(node, data) {
data = '<div>' + EscapeHtmlBreaks(data) + '</div>'
if (node.consoleText == undefined) { node.consoleText = data; } else { node.consoleText += data; }
if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
if (consoleNode == node) {
Q('p15agentConsole').innerHTML += data;
Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
@ -4032,7 +4059,7 @@
filetreelinkpath = '';
for (var i in filetreelocation) {
if ((filetreex.f != undefined) && (filetreex.f[filetreelocation[i]] != undefined)) {
if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) {
filetreelocation2.push(filetreelocation[i]);
fullPath += ' / ' + filetreelocation[i];
if ((folderdepth == 1)) {
@ -4044,7 +4071,7 @@
if (filetreelinkpath != '') { filetreelinkpath += '/' + filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + filetreelocation[i]; } }
}
filetreex = filetreex.f[filetreelocation[i]];
displayPath += ' / <a style=cursor:pointer onclick=p5folderup(' + folderdepth + ')>' + (filetreex.n != undefined?filetreex.n:filetreelocation[i]) + '</a>';
displayPath += ' / <a style=cursor:pointer onclick=p5folderup(' + folderdepth + ')>' + (filetreex.n != null?filetreex.n:filetreelocation[i]) + '</a>';
folderdepth++;
} else {
break;
@ -4066,11 +4093,11 @@
// Figure out the date
var fdatestr = '';
if (f.d != undefined) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
if (f.d != null) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
// Figure out the size
var fsize = '';
if (f.s != undefined) { fsize = getFileSizeStr(f.s); }
if (f.s != null) { fsize = getFileSizeStr(f.s); }
var h = '';
if (f.t < 3) {
@ -4087,7 +4114,7 @@
if (f.t < 3) { html1 += h; } else { html2 += h; }
}
//if (f.parent == undefined) { }
//if (f.parent == null) { }
QH('p5rightOfButtons', p5getQuotabar(filetreex));
QH('p5files', html1 + html2);
@ -4108,7 +4135,7 @@
function p5getQuotabar(f) {
while (f.t > 1) { f = f.parent; }
if ((f.t != 1) || (f.maxbytes == undefined)) return '';
if ((f.t != 1) || (f.maxbytes == null)) return '';
var tf = Math.floor(f.s / 1024), tq = Math.floor((f.maxbytes - f.s) / 1024);
return '<span title="' + tf + "k in " + f.c + " file" + (f.c > 1?'s':'') + ". " + (Math.floor(f.maxbytes / 1024)) + 'k maxinum">' + ((tq < 0)?('Storage limit exceed'):(tq + 'k remaining')) + ' <progress style=height:10px;width:200px value=' + f.s + ' max=' + f.maxbytes + ' /></span>';
}
@ -4122,7 +4149,7 @@
function p5sort_files(files) {
var r = [], sortselection = Q('p5sortdropdown').value;
for (var i in files) { files[i].nx = i; if (files[i].n == undefined) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
for (var i in files) { files[i].nx = i; if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
sortorder = 1;
if (sortselection > 3) { sortorder = -1; sortselection -= 3; }
if (sortselection == 1) { r.sort(p5sort_filename); }
@ -4144,9 +4171,9 @@
function getFileSelCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) cc++; } return cc; }
function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; }
function p5selectallfile() { var nv = (getFileSelCount() == 0), checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p5setActions(); }
function setupBackPointers(x) { if (x.f != undefined) { var fs = 0, fc = 0; for (var i in x.f) { setupBackPointers(x.f[i]); x.f[i].parent = x; if (x.f[i].s) { fs += x.f[i].s; } if (x.f[i].c) { fc += x.f[i].c; } if (x.f[i].t == 3) { fc++; } } x.s = fs; x.c = fc; } return x; }
function setupBackPointers(x) { if (x.f != null) { var fs = 0, fc = 0; for (var i in x.f) { setupBackPointers(x.f[i]); x.f[i].parent = x; if (x.f[i].s) { fs += x.f[i].s; } if (x.f[i].c) { fc += x.f[i].c; } if (x.f[i].t == 3) { fc++; } } x.s = fs; x.c = fc; } return x; }
function getFileSizeStr(size) { if (size == 1) return "1 byte"; return "" + size + " bytes"; }
function p5folderup(x) { if (x == undefined) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); }
function p5folderup(x) { if (x == null) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); }
function p5folderset(x) { filetreelocation.push(decodeURIComponent(x)); updateFiles(); }
function p5createfolder() { setDialogMode(2, "New Folder", 3, p5createfolderEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck() style=width:100% />'); Q('p5renameinput').focus(); }
function p5createfolderEx() { meshserver.Send({ action: 'fileoperation', fileop: 'createfolder', path: filetreelocation, newfolder: Q('p5renameinput').value}); }
@ -4277,7 +4304,7 @@
}
if (msg != '') msg += ', ';
if (user.name != userinfo.name) { msg += "<a onclick=showUserAdminDialog(event,\"" + user._id + "\")>"; }
if ((user.siteadmin == undefined) || (user.siteadmin == 0)) {
if ((user.siteadmin == null) || (user.siteadmin == 0)) {
msg += "User";
} else if (user.siteadmin == 8) {
msg += "User with server files";
@ -4286,7 +4313,7 @@
} else {
msg += "Partial Admin";
}
if ((user.quota != undefined) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
if (user.name != userinfo.name) { msg += "</a>"; }
if (user.email != null) {
msg = '<table style=width:100%><tr><td>' + EscapeHtml(user.name) + ', <a onclick=doemail(event,\"' + user.email + '\")>' + user.email + '</a>' + (((serverinfo.emailcheck == true) && (user.emailVerified != true))?' (unverified)':'') + '<td align=right>' + msg + '</table>';
@ -4370,7 +4397,7 @@
QE('ua_serverrestore', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_fileaccess', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_serverupdate', userinfo.siteadmin == 0xFFFFFFFF);
Q('ua_fileaccessquota').value = (user.quota != undefined)?(user.quota / 1024):'';
Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):'';
showUserAdminDialogValidate();
return false;
}
@ -4429,7 +4456,7 @@
d3filetreelinkpath = '';
for (var i in d3filetreelocation) {
if ((filetreex.f != undefined) && (filetreex.f[d3filetreelocation[i]] != undefined)) {
if ((filetreex.f != null) && (filetreex.f[d3filetreelocation[i]] != null)) {
d3filetreelocation2.push(d3filetreelocation[i]);
if ((folderdepth == 1)) {
var sp = d3filetreelocation[i].split('/');
@ -4459,7 +4486,7 @@
// Figure out the size
var fsize = '';
if (f.s != undefined) { fsize = getFileSizeStr(f.s); }
if (f.s != null) { fsize = getFileSizeStr(f.s); }
var h = '';
if (f.t < 3) {
@ -4480,7 +4507,7 @@
}
function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); }
function d3folderup(x) { if (x == undefined) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
function d3getFileSel() { var cc = []; var checkboxes = document.getElementsByName('fcx'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { cc.push(checkboxes[i].value) } } return cc; }
function d3setActions() {
var mode = Q('d3uploadMode').value;
@ -4523,7 +4550,7 @@
var t = '';
var d = new Date(n.time);
var icon = 0;
if (n.nodeid != undefined) {
if (n.nodeid != null) {
var node = getNodeFromId(n.nodeid);
if (node != null) {
//console.log(node);
@ -4546,7 +4573,7 @@
for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
if (j != -1) {
var n = notifications[j];
if (n.nodeid != undefined) {
if (n.nodeid != null) {
if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
@ -4578,8 +4605,8 @@
// Add a new notification and play the notification sound
function addNotification(n) {
if (n.time == undefined) { n.time = Date.now(); }
if (n.id == undefined) { n.id = Math.random(); }
if (n.time == null) { n.time = Date.now(); }
if (n.id == null) { n.id = Math.random(); }
notifications.unshift(n);
setNotificationCount(notifications.length);
Q('chimes').play();
@ -4598,7 +4625,7 @@
// POPUP DIALOG
//
// undefined = Hidden, 1 = Generic Message
// null = Hidden, 1 = Generic Message
var xxdialogMode;
var xxdialogFunc;
var xxdialogButtons;
@ -4662,8 +4689,8 @@
// Generic methods
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('/'); }
function putstore(name, val) { try { if (typeof (localStorage) === "undefined") return; localStorage.setItem(name, val); } catch (e) { } }
function getstore(name, val) { try { if (typeof (localStorage) === "undefined") return val; var v = localStorage.getItem(name); if ((v == undefined) || (v == null)) return val; return v; } catch (e) { return val; } }
function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } }
function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
function addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>&diams; " + x + "</a>"; }
function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }

View File

@ -271,11 +271,11 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
obj.authenticate(req.body.username, req.body.password, domain, function (err, userid, passhint) {
if (userid) {
var user = obj.users[userid];
// Save login time
user.login = Date.now();
obj.db.SetUser(user);
// Regenerate session when signing in to prevent fixation
req.session.regenerate(function () {
// Store the user's primary key in the session store to be retrieved, or in this case the entire user object
@ -302,7 +302,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
res.redirect(domain.url);
}
});
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id })
} else {
delete req.session.loginmode;
@ -407,7 +407,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
var domain = checkUserIpAddress(req, res);
if (domain == null) return;
if (req.query.c != null) {
var cookie = obj.decodeCookie(req.query.c, null, 30);
var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30);
if ((cookie != null) && (cookie.u != null) && (cookie.e != null)) {
var idsplit = cookie.u.split('/');
if ((idsplit.length != 2) || (idsplit[0] != domain.id)) {
@ -444,10 +444,13 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
delete userinfo.domain;
delete userinfo.subscriptions;
delete userinfo.passtype;
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Verified email of user ' + EscapeHtml(user.name) + ' (' + userinfo.email + ')', domain: domain.id })
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Verified email of user ' + EscapeHtml(user.name) + ' (' + EscapeHtml(userinfo.email) + ')', domain: domain.id })
// Send the confirmation page
res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'Verified e-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
// Send a notification
obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified: <b>' + EscapeHtml(userinfo.email) + '</b>.' , nolog: 1 })
}
});
}
@ -571,6 +574,15 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
req.session.domainid = domain.id;
req.session.currentNode = '';
} else if (req.query.login && (obj.parent.loginCookieEncryptionKey != null)) {
var loginCookie = obj.parent.decodeCookie(req.query.login, obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
if ((loginCookie != null) && (loginCookie.a == 3) && (loginCookie.u != null) && (loginCookie.u.split('/')[1] == domain.id)) {
// If a login cookie was provided, setup the session here.
if (req.session && req.session.loginmode) { delete req.session.loginmode; }
req.session.userid = loginCookie.u;
req.session.domainid = domain.id;
req.session.currentNode = '';
}
}
// If a user is logged in, serve the default app, otherwise server the login app.
if (req.session && req.session.userid) {
@ -579,11 +591,15 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
if (req.session.viewmode) {
viewmode = req.session.viewmode;
delete req.session.viewmode;
} else if (req.query.viewmode) {
viewmode = req.query.viewmode;
}
var currentNode = '';
if (req.session.currentNode) {
currentNode = req.session.currentNode;
delete req.session.currentNode;
} else if (req.query.node) {
currentNode = 'node/' + domain.id + '/' + req.query.node;
}
var user;
var logoutcontrol;
@ -894,7 +910,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
} else {
// Get the session from the cookie
if (obj.parent.multiServer == null) { return; }
var session = obj.decodeCookie(req.query.auth);
var session = obj.parent.decodeCookie(req.query.auth);
if (session == null) { console.log('ERR: Invalid cookie'); return; }
if (session.domainid != domain.id) { console.log('ERR: Invalid domain'); return; }
user = obj.users[session.userid];
@ -1484,7 +1500,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
}
// Force mesh agent disconnection
function forceMeshAgentDisconnect(user, domain, nodeid, disconnectMode) {
obj.forceMeshAgentDisconnect = function(user, domain, nodeid, disconnectMode) {
if (nodeid == null) return;
var splitnode = nodeid.split('/');
if ((splitnode.length != 3) || (splitnode[1] != domain.id)) return; // Check that nodeid is valid and part of our domain
@ -1642,40 +1658,5 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
}
}
// Generate a cryptographic key used to encode and decode cookies
obj.generateCookieKey = function () {
return new Buffer(obj.crypto.randomBytes(32), 'binary');
//return Buffer.alloc(32, 0); // Sets the key to zeros, debug only.
}
// Encode an object as a cookie using a key. (key must be 32 bytes long)
obj.encodeCookie = function (o, key) {
try {
if (key == null) { key = obj.serverKey; }
o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time
var iv = new Buffer(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key, iv);
var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]);
return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
} catch (e) { return null; }
}
// Decode a cookie back into an object using a key. Return null if it's not a valid cookie. (key must be 32 bytes long)
obj.decodeCookie = function (cookie, key, timeout) {
try {
if (key == null) { key = obj.serverKey; }
cookie = new Buffer(cookie.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64');
var decipher = obj.crypto.createDecipheriv('aes-256-gcm', key, cookie.slice(0, 12));
decipher.setAuthTag(cookie.slice(12, 16));
var o = JSON.parse(decipher.update(cookie.slice(28), 'binary', 'utf8') + decipher.final('utf8'));
if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { return null; }
o.time = o.time * 1000; // Decode the cookie creation time
o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
if (timeout == null) { timeout = 2; }
if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
return o;
} catch (e) { return null; }
}
obj.serverKey = obj.generateCookieKey();
return obj;
}