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

Ylian Saint-Hilaire committed Dec 13, 2017 at 14:52 UTC 9501ffd60983231b754bbbc0e0e41ad77ea0d15a
12 files changed +315 -211
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
db.js
+1 -1
@@ -80,7 +80,7 @@ module.exports.CreateDB = function (args, datapath) {
80 */
81 }
82
83 - obj.Set = function (data) { obj.file.update({ _id: data._id }, data, { upsert: true }); }
83 + obj.Set = function (data, func) { obj.file.update({ _id: data._id }, data, { upsert: true }, func); }
84 obj.Get = function (id, func) { obj.file.find({ _id: id }, func); }
85 obj.GetAll = function (func) { obj.file.find({}, func); }
86 obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type : 0 }, func); }
meshagent.js
+2 -1
@@ -38,6 +38,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
38 obj.close = function (arg) {
39 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
40 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
41 + if (arg == 3) { obj.authenticated = -1; } // Don't communicate with this agent anymore, but don't disconnect (Duplicate agent).
42 if (obj.parent.wsagents[obj.dbNodeKey] == obj) {
43 delete obj.parent.wsagents[obj.dbNodeKey];
44 obj.parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1);
@@ -293,7 +294,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
294 if (dupAgent) {
295 // Close the duplicate agent
296 obj.parent.parent.debug(1, 'Duplicate agent ' + obj.nodeid + ' (' + obj.remoteaddr + ')');
296 - dupAgent.close();
297 + dupAgent.close(3);
298 } else {
299 // Indicate the agent is connected
300 obj.parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1);
meshcentral.js
+89 -7
@@ -40,6 +40,8 @@ function CreateMeshCentralServer() {
40 obj.maintenanceTimer = null;
41 obj.serverId = null;
42 obj.currentVer = null;
43 + obj.serverKey = new Buffer(obj.crypto.randomBytes(32), 'binary');
44 + obj.loginCookieEncryptionKey = null;
45 try { obj.currentVer = JSON.parse(require('fs').readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
46
47 // Setup the default configuration and files paths
@@ -70,7 +72,7 @@ function CreateMeshCentralServer() {
72 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.
73
74 // Check for invalid arguments
73 - 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'];
75 + 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'];
76 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; } }
77 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; }
78
@@ -234,6 +236,8 @@ function CreateMeshCentralServer() {
236 if (obj.args.showevents) { obj.db.GetAllType('event', function (err, docs) { console.log(docs); process.exit(); }); return; }
237 if (obj.args.showpower) { obj.db.GetAllType('power', function (err, docs) { console.log(docs); process.exit(); }); return; }
238 if (obj.args.showiplocations) { obj.db.GetAllType('iploc', function (err, docs) { console.log(docs); process.exit(); }); return; }
239 + if (obj.args.logintoken) { obj.getLoginToken(obj.args.logintoken, function (r) { console.log(r); process.exit(); }); return; }
240 + if (obj.args.logintokenkey) { obj.showLoginTokenKey(function (r) { console.log(r); process.exit(); }); return; }
241 if (obj.args.dbexport) {
242 // Export the entire database to a JSON file
243 if (obj.args.dbexport == true) { obj.args.dbexport = obj.path.join(obj.datapath, 'meshcentral.db.json'); }
@@ -365,13 +369,18 @@ function CreateMeshCentralServer() {
369 // Dispatch an event that the server is now running
370 obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' })
371
368 - obj.debug(1, 'Server started');
372 + // Load the login cookie encryption key from the database if allowed
373 + if ((obj.config) && (obj.config.settings) && (obj.config.settings.loginTokenOk == true)) {
374 + obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
375 + if ((docs.length > 0) && (docs[0].key != null)) {
376 + obj.loginCookieEncryptionKey = Buffer.from(docs[0].key, 'hex');
377 + } else {
378 + obj.loginCookieEncryptionKey = obj.generateCookieKey(); obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() });
379 + }
380 + });
381 + }
382
370 - /*
371 - obj.db.GetUserWithVerifiedEmail('', 'ylian.saint-hilaire@intel.com', function (err, docs) {
372 - console.log(JSON.stringify(docs));
373 - });
374 - */
383 + obj.debug(1, 'Server started');
384 });
385 });
386 });
@@ -823,6 +832,79 @@ function CreateMeshCentralServer() {
832 }
833 }
834
835 + // Generate a time limited user login token
836 + obj.getLoginToken = function (userid, func) {
837 + var x = userid.split('/');
838 + if (x == null || x.length != 3 || x[0] != 'user') { func('Invalid userid.'); return; }
839 + obj.db.Get(userid, function (err, docs) {
840 + if (err != null || docs == null || docs.length == 0) {
841 + func('User ' + userid + ' not found.'); return;
842 + } else {
843 + // Load the login cookie encryption key from the database
844 + obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
845 + if ((docs.length > 0) && (docs[0].key != null)) {
846 + // Key is present, use it.
847 + obj.loginCookieEncryptionKey = Buffer.from(docs[0].key, 'hex');
848 + func(obj.encodeCookie({ u: userid, a: 3 }, obj.loginCookieEncryptionKey));
849 + } else {
850 + // Key is not present, generate one.
851 + obj.loginCookieEncryptionKey = obj.generateCookieKey();
852 + obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.encodeCookie({ u: userid, a: 3 }, obj.loginCookieEncryptionKey)); });
853 + }
854 + });
855 + }
856 + });
857 + }
858 +
859 + // Show the yser login token generation key
860 + obj.showLoginTokenKey = function (func) {
861 + // Load the login cookie encryption key from the database
862 + obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
863 + if ((docs.length > 0) && (docs[0].key != null)) {
864 + // Key is present, use it.
865 + func(docs[0].key);
866 + } else {
867 + // Key is not present, generate one.
868 + obj.loginCookieEncryptionKey = obj.generateCookieKey();
869 + obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.loginCookieEncryptionKey.toString('hex')); });
870 + }
871 + });
872 + }
873 +
874 + // Generate a cryptographic key used to encode and decode cookies
875 + obj.generateCookieKey = function () {
876 + return new Buffer(obj.crypto.randomBytes(32), 'binary');
877 + //return Buffer.alloc(32, 0); // Sets the key to zeros, debug only.
878 + }
879 +
880 + // Encode an object as a cookie using a key. (key must be 32 bytes long)
881 + obj.encodeCookie = function (o, key) {
882 + try {
883 + if (key == null) { key = obj.serverKey; }
884 + o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time
885 + var iv = new Buffer(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key, iv);
886 + var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]);
887 + return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
888 + } catch (e) { return null; }
889 + }
890 +
891 + // 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)
892 + obj.decodeCookie = function (cookie, key, timeout) {
893 + try {
894 + if (key == null) { key = obj.serverKey; }
895 + cookie = new Buffer(cookie.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64');
896 + var decipher = obj.crypto.createDecipheriv('aes-256-gcm', key, cookie.slice(0, 12));
897 + decipher.setAuthTag(cookie.slice(12, 16));
898 + var o = JSON.parse(decipher.update(cookie.slice(28), 'binary', 'utf8') + decipher.final('utf8'));
899 + if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { return null; }
900 + o.time = o.time * 1000; // Decode the cookie creation time
901 + o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
902 + if (timeout == null) { timeout = 2; }
903 + 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)
904 + return o;
905 + } catch (e) { return null; }
906 + }
907 +
908 // Debug
909 obj.debug = function (lvl) {
910 if (lvl > obj.debugLevel) return;
meshmail.js
+17 -4
@@ -11,6 +11,7 @@ module.exports.CreateMeshMain = function (parent) {
11 obj.parent = parent;
12 obj.retry = 0;
13 obj.sendingMail = false;
14 + obj.mailCookieEncryptionKey = null;
15 const nodemailer = require('nodemailer');
16
17 // Default account email validation mail
@@ -45,7 +46,7 @@ module.exports.CreateMeshMain = function (parent) {
46 // Send account check mail
47 obj.sendAccountCheckMail = function (domain, username, email) {
48 if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, no reset possible.
48 - var cookie = obj.parent.webserver.encodeCookie({ u: domain.id + '/' + username, e: email, a: 1 });
49 + var cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 1 }, obj.mailCookieEncryptionKey);
50 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) });
51 sendNextMail();
52 }
@@ -53,7 +54,7 @@ module.exports.CreateMeshMain = function (parent) {
54 // Send account reset mail
55 obj.sendAccountResetMail = function (domain, username, email) {
56 if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, don't validate the email address.
56 - var cookie = obj.parent.webserver.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 });
57 + var cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 }, obj.mailCookieEncryptionKey);
58 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) });
59 sendNextMail();
60 }
@@ -74,7 +75,7 @@ module.exports.CreateMeshMain = function (parent) {
75 sendNextMail(); // Send the next mail
76 } else {
77 obj.retry++;
77 - console.log('SMTP server failed: ' + err.response);
78 + console.log('SMTP server failed: ' + JSON.stringify(err));
79 if (obj.retry < 6) { setTimeout(sendNextMail, 60000); } // Wait and try again
80 }
81 });
@@ -86,10 +87,22 @@ module.exports.CreateMeshMain = function (parent) {
87 if (err == null) {
88 console.log('SMTP mail server ' + parent.config.smtp.host + ' working as expected.');
89 } else {
89 - console.log('SMTP mail server ' + parent.config.smtp.host + ' failed: ' + err.response);
90 + console.log('SMTP mail server ' + parent.config.smtp.host + ' failed: ' + JSON.stringify(err));
91 }
92 });
93 }
94
95 + // Load the cookie encryption key from the database
96 + obj.parent.db.Get('MailCookieEncryptionKey', function (err, docs) {
97 + if ((docs.length > 0) && (docs[0].key != null)) {
98 + // Key is present, use it.
99 + obj.mailCookieEncryptionKey = Buffer.from(docs[0].key, 'hex');
100 + } else {
101 + // Key is not present, generate one.
102 + obj.mailCookieEncryptionKey = obj.parent.generateCookieKey();
103 + obj.parent.db.Set({ _id: 'MailCookieEncryptionKey', key: obj.mailCookieEncryptionKey.toString('hex'), time: Date.now() });
104 + }
105 + });
106 +
107 return obj;
108 }
\ No newline at end of file
meshrelay.js
+1 -1
@@ -84,7 +84,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
84 }
85 } else {
86 // Get the session from the cookie
87 - var cookie = obj.parent.parent.webserver.decodeCookie(req.query.auth);
87 + var cookie = obj.parent.parent.decodeCookie(req.query.auth);
88 if (cookie != null) {
89 obj.authenticated = true;
90 if (cookie.tcpport != null) {
meshuser.js
+2 -2
@@ -833,7 +833,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
833 case 'agentdisconnect':
834 {
835 // Force mesh agent disconnection
836 - forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
836 + obj.parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
837 break;
838 }
839 case 'close':
@@ -855,7 +855,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
855 if (command.nodeid) { cookieContent.nodeid = command.nodeid; }
856 if (command.tcpaddr) { cookieContent.tcpaddr = command.tcpaddr; } // Indicates the browser want to agent to TCP connect to a remote address
857 if (command.tcpport) { cookieContent.tcpport = command.tcpport; } // Indicates the browser want to agent to TCP connect to a remote port
858 - command.cookie = obj.parent.encodeCookie(cookieContent);
858 + command.cookie = obj.parent.parent.encodeCookie(cookieContent);
859 ws.send(JSON.stringify(command));
860 }
861 }
multiserver.js
+1 -1
@@ -561,7 +561,7 @@ module.exports.CreateMultiServer = function (parent, args) {
561 if (path.substring(path.length - 11) == '/.websocket') { path = path.substring(0, path.length - 11); }
562 var queryStr = ''
563 for (var i in req.query) { queryStr += ((queryStr == '') ? '?' : '&') + i + '=' + req.query[i]; }
564 - if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); }
564 + if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.parent.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); }
565 var url = obj.peerConfig.servers[serverid].url + path + queryStr;
566
567 // Setup an connect the web socket
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.0-o",
3 + "version": "0.1.0-x",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
+178 -151
@@ -56,41 +56,43 @@
56 <div style="float:right">
57 <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>
58 </div>
59 - <p>{{{logoutControl}}}</p>
59 + <p id="logoutControl">{{{logoutControl}}}</p>
60 </div>
61 - <div id=topbar class=noselect style=display:none>
62 - <div>
61 + <div id=topbarmaster>
62 + <div id=topbar class=noselect style=display:none>
63 <div>
64 - <table style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
65 - <tr>
66 - <td id=MainMenuMyDevices style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(1)>My Devices</td>
67 - <td id=MainMenuMyAccount style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(2)>My Account</td>
68 - <td id=MainMenuMyEvents style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(3)>My Events</td>
69 - <td id=MainMenuMyFiles style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(5)>My Files</td>
70 - <td id=MainMenuMyUsers style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(4)>My Users</td>
71 - <td class=style3 style=height:24px>&nbsp;</td>
72 - </tr>
73 - </table>
74 - <div id="MainSubMenuSpan" style=display:none>
75 - <table id="MainSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
76 - <tr>
77 - <td id=MainDev style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(10)>General</td>
78 - <td id=MainDevDesktop style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(11)>Desktop</td>
79 - <td id=MainDevTerminal style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(12)>Terminal</td>
80 - <td id=MainDevFiles style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(13)>Files</td>
81 - <td id=MainDevAmt style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(14)>Intel&reg; AMT</td>
82 - <td id=MainDevConsole style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(15)>Console</td>
83 - <td class=style3 style=height:24px>&nbsp;</td>
84 - </tr>
85 - </table>
86 - </div>
87 - <div id="MeshSubMenuSpan" style=display:none>
88 - <table id="MeshSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
64 + <div>
65 + <table style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
66 <tr>
90 - <td id=MeshGeneral style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(20)>General</td>
67 + <td id=MainMenuMyDevices style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(1)>My Devices</td>
68 + <td id=MainMenuMyAccount style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(2)>My Account</td>
69 + <td id=MainMenuMyEvents style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(3)>My Events</td>
70 + <td id=MainMenuMyFiles style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(5)>My Files</td>
71 + <td id=MainMenuMyUsers style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(4)>My Users</td>
72 <td class=style3 style=height:24px>&nbsp;</td>
73 </tr>
74 </table>
75 + <div id="MainSubMenuSpan" style=display:none>
76 + <table id="MainSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
77 + <tr>
78 + <td id=MainDev style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(10)>General</td>
79 + <td id=MainDevDesktop style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(11)>Desktop</td>
80 + <td id=MainDevTerminal style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(12)>Terminal</td>
81 + <td id=MainDevFiles style=width:100px;height:24px;cursor:pointer;display:none class=style3 onclick=go(13)>Files</td>
82 + <td id=MainDevAmt style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(14)>Intel&reg; AMT</td>
83 + <td id=MainDevConsole style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(15)>Console</td>
84 + <td class=style3 style=height:24px>&nbsp;</td>
85 + </tr>
86 + </table>
87 + </div>
88 + <div id="MeshSubMenuSpan" style=display:none>
89 + <table id="MeshSubMenu" style="width: 100%; height: 22px;" cellpadding=0 cellspacing=0 class=style1>
90 + <tr>
91 + <td id=MeshGeneral style=width:100px;height:24px;cursor:pointer class=style3 onclick=go(20)>General</td>
92 + <td class=style3 style=height:24px>&nbsp;</td>
93 + </tr>
94 + </table>
95 + </div>
96 </div>
97 </div>
98 </div>
@@ -265,7 +267,9 @@
267 <table style="width:100%" cellpadding="0" cellspacing="0">
268 <tr>
269 <td style=width:auto valign=top>
268 - <h1><span id=p10deviceName></span> - General</h1>
270 + <div id="p10title">
271 + <h1><span id=p10deviceName></span> - General</h1>
272 + </div>
273 <div id=p10html></div>
274 </td>
275 <td style=width:20px></td>
@@ -279,7 +283,9 @@
283 <div id=p10html3></div>
284 </div>
285 <div id=p11 style=display:none>
282 - <h1 id=p11deviceNameHeader><span id=p11deviceName></span> - Desktop</h1>
286 + <div id="p11title">
287 + <h1 id=p11deviceNameHeader><span id=p11deviceName></span> - Desktop</h1>
288 + </div>
289 <div id="p14warning" style='max-width:100%;display:none;cursor:pointer;margin-bottom:5px' onclick="showFeaturesDlg()">
290 <div class=icon2 style="float:left;margin:7px"></div>
291 <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 @@
340 </table>
341 </div>
342 <div id=p12 style=display:none>
337 - <h1><span id=p12deviceName></span> - Terminal</h1>
343 + <div id="p12title"><h1><span id=p12deviceName></span> - Terminal</h1></div>
344 <div id="p12warning" style='max-width:100%;display:none;cursor:pointer;margin-bottom:5px' onclick=showFeaturesDlg()>
345 <div class="icon2" style="float:left;margin:7px"></div>
346 <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 @@
395 </table>
396 </div>
397 <div id=p13 style=display:none>
392 - <h1><span id=p13deviceName></span> - Files</h1>
398 + <div id="p13title"><h1><span id=p13deviceName></span> - Files</h1></div>
399 <table id="p13toolbar" style="width: 100%" cellpadding="0" cellspacing="0">
400 <tr>
401 <td style="background-color:#C0C0C0;border-bottom:2px solid black;padding:2px">
@@ -443,11 +449,11 @@
449 </table>
450 </div>
451 <div id=p14 style=display:none>
446 - <h1><span id=p14deviceName></span> - Intel&reg; AMT</h1>
452 + <div id="p14title"><h1><span id=p14deviceName></span> - Intel&reg; AMT</h1></div>
453 <iframe id=p14iframe style="width:100%;height:650px;border:0;overflow:hidden" src="/commander.htm"></iframe>
454 </div>
455 <div id=p15 style=display:none>
450 - <h1><span id=p15deviceName></span> - Console</h1>
456 + <div id="p15title"><h1><span id=p15deviceName></span> - Console</h1></div>
457 <table cellpadding=0 cellspacing=0 style="width:100%;padding:0px;padding:0px;margin-top:0px">
458 <tr>
459 <td style=background:#C0C0C0>
@@ -494,7 +500,7 @@
500 <tr>
501 <td style="text-align:left"></td>
502 <td style="text-align:right">
497 - <a id="verifyEmailId2" style="color:yellow;margin-left:3px;cursor:pointer" onclick="account_showVerifyEmail()">Verify Email</a>
503 + <a id="verifyEmailId2" style="color:yellow;margin-left:3px;cursor:pointer;display:none" onclick="account_showVerifyEmail()">Verify Email</a>
504 <a style="margin-left:3px" href="terms">Terms &amp; Privacy</a>
505 </td>
506 </tr>
@@ -599,6 +605,7 @@
605 </div>
606 </div>
607 <script type="text/javascript">
608 + var args;
609 var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
610 var StatusStrs = ['Disconnected', 'Connecting...', 'Setup...', 'Connected', 'Intel&reg; AMT Connected'];
611 var sort = 0;
@@ -635,13 +642,25 @@
642 if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
643
644 // Check if we are in debug mode
638 - var args = parseUriArgs();
645 + args = parseUriArgs();
646 debugmode = (args.debug == 1);
647 QV('p13AutoConnect', debugmode); // Files
648 QV('autoconnectbutton2', debugmode); // Terminal
649 QV('autoconnectbutton1', debugmode); // Desktop
650
651 // Setup page visuals
652 + if (args.hide) {
653 + var hide = parseInt(args.hide);
654 + QV('masthead', !(hide & 1));
655 + QV('topbarmaster', !(hide & 2));
656 + QV('footer', !(hide & 4));
657 + QV('p10title', !(hide & 8));
658 + QV('p11title', !(hide & 8));
659 + QV('p12title', !(hide & 8));
660 + QV('p13title', !(hide & 8));
661 + QV('p14title', !(hide & 8));
662 + QV('p15title', !(hide & 8));
663 + }
664 p1updateInfo();
665
666 // Setup the context menu
@@ -733,6 +752,8 @@
752 powerTimelineUpdate = null;
753 deleteAllNotifications(); // Close and clear notifications if present
754 hideContextMenu(); // Hide the context menu if present
755 + QV('verifyEmailId2', false);
756 + QV('logoutControl', false);
757 } else if (state == 2) {
758 // Fetch list of meshes, nodes, files
759 meshserver.Send({ action: 'meshes' });
@@ -850,7 +871,7 @@
871 }
872 case 'msg': {
873 // Check if this is a message from a node
853 - if (message.nodeid != undefined) {
874 + if (message.nodeid != null) {
875 var index = -1;
876 for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } }
877 if (index != -1) {
@@ -858,15 +879,15 @@
879 if (message.type == 'console') { p15consoleReceive(nodes[index], message.value); } // This is a console message.
880 if (message.type == 'notify') { // This is a notification message.
881 var n = { text:message.value };
861 - if (message.nodeid != undefined) { n.nodeid = message.nodeid; }
862 - if (message.tag != undefined) { n.tag = message.tag; }
882 + if (message.nodeid != null) { n.nodeid = message.nodeid; }
883 + if (message.tag != null) { n.tag = message.tag; }
884 addNotification(n);
885 }
886 }
887 } else {
888 if (message.type == 'notify') { // This is a notification message.
889 var n = { text:message.value };
869 - if (message.tag != undefined) { n.tag = message.tag; }
890 + if (message.tag != null) { n.tag = message.tag; }
891 addNotification(n);
892 }
893 }
@@ -956,7 +977,7 @@
977 }
978 case 'createmesh': {
979 // A new mesh was created
959 - 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.
980 + 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.
981 meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
982 updateMeshes();
983 updateDevices();
@@ -966,7 +987,7 @@
987 }
988 case 'meshchange': {
989 // Update mesh information
969 - if (meshes[message.event.meshid] == undefined) {
990 + if (meshes[message.event.meshid] == null) {
991 // This is a new mesh for us
992 meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
993 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 @@
998 meshes[message.event.meshid].links = message.event.links;
999
1000 // Check if we lost rights to this mesh in this change.
980 - if (meshes[message.event.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()] == undefined) {
1001 + if (meshes[message.event.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()] == null) {
1002 if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
1003 delete meshes[message.event.meshid];
1004
@@ -1065,21 +1086,21 @@
1086 node.wifiloc = message.event.node.wifiloc;
1087 node.gpsloc = message.event.node.gpsloc;
1088 node.userloc = message.event.node.userloc;
1068 - if (message.event.node.agent != undefined) {
1069 - if (node.agent == undefined) node.agent = {};
1070 - if (message.event.node.agent.ver != undefined) { node.agent.ver = message.event.node.agent.ver; }
1071 - if (message.event.node.agent.id != undefined) { node.agent.id = message.event.node.agent.id; }
1072 - if (message.event.node.agent.caps != undefined) { node.agent.caps = message.event.node.agent.caps; }
1073 - if (message.event.node.agent.core != undefined) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
1089 + if (message.event.node.agent != null) {
1090 + if (node.agent == null) node.agent = {};
1091 + if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; }
1092 + if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; }
1093 + if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; }
1094 + if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
1095 node.agent.tag = message.event.node.agent.tag;
1096 }
1076 - if (message.event.node.intelamt != undefined) {
1077 - if (node.intelamt == undefined) node.intelamt = {};
1078 - if (message.event.node.intelamt.host != undefined) { node.intelamt.user = message.event.node.intelamt.host; }
1079 - if (message.event.node.intelamt.user != undefined) { node.intelamt.user = message.event.node.intelamt.user; }
1080 - if (message.event.node.intelamt.tls != undefined) { node.intelamt.tls = message.event.node.intelamt.tls; }
1081 - if (message.event.node.intelamt.ver != undefined) { node.intelamt.ver = message.event.node.intelamt.ver; }
1082 - if (message.event.node.intelamt.state != undefined) { node.intelamt.state = message.event.node.intelamt.state; }
1097 + if (message.event.node.intelamt != null) {
1098 + if (node.intelamt == null) node.intelamt = {};
1099 + if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; }
1100 + if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; }
1101 + if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; }
1102 + if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; }
1103 + if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
1104 }
1105 node.namel = node.name.toLowerCase();
1106 if (node.host) { node.hostl = node.host.toLowerCase(); } else { node.hostl = node.namel; }
@@ -1134,7 +1155,7 @@
1155 }
1156 case 'scanamtdevice': {
1157 // Populate the Intel AMT scan dialog box with the result of the RMCP scan
1137 - if ((xxdialogMode == undefined) || (!Q('dp1range')) || (Q('dp1range').value != message.event.range)) return;
1158 + if ((xxdialogMode == null) || (!Q('dp1range')) || (Q('dp1range').value != message.event.range)) return;
1159 var x = '';
1160 if (message.event.results == null) {
1161 // The scan could not occur because of an error. Likely the user range was invalid.
@@ -1158,6 +1179,12 @@
1179 QE('dp1rangebutton', true);
1180 break;
1181 }
1182 + case 'notify': {
1183 + var n = { text: message.event.value };
1184 + if (message.event.tag != null) { n.tag = message.event.tag; }
1185 + addNotification(n);
1186 + break;
1187 + }
1188 }
1189 break;
1190 }
@@ -1275,7 +1302,7 @@
1302 for (var i in nodes) {
1303 if (nodes[i].v == false) continue;
1304 var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
1278 - if (meshlinks == undefined) continue;
1305 + if (meshlinks == null) continue;
1306 if (sort == 0) {
1307 // Mesh header
1308 if (nodes[i].meshid != current) {
@@ -1312,9 +1339,9 @@
1339
1340 var title = EscapeHtml(nodes[i].name);
1341 if (title.length == 0) { title = '<i>None</i>'; }
1315 - if ((nodes[i].host != undefined) && (nodes[i].host.length > 0)) { title += " / " + EscapeHtml(nodes[i].host); }
1342 + if ((nodes[i].host != null) && (nodes[i].host.length > 0)) { title += " / " + EscapeHtml(nodes[i].host); }
1343 var name = EscapeHtml(nodes[i].name);
1317 - if (showHostnames == true && nodes[i].host != undefined) name = EscapeHtml(nodes[i].host);
1344 + if (showHostnames == true && nodes[i].host != null) name = EscapeHtml(nodes[i].host);
1345 if (name.length == 0) { name = '<i>None</i>'; }
1346
1347 // Node
@@ -1336,9 +1363,9 @@
1363 if (sort == 0 && Q('SearchInput').value == '') {
1364 for (var i in meshes) {
1365 var mesh = meshes[i], meshlink = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
1339 - if (meshlink != undefined) {
1366 + if (meshlink != null) {
1367 var meshrights = meshlink.rights;
1341 - if (displayedMeshes[mesh._id] == undefined) {
1368 + if (displayedMeshes[mesh._id] == null) {
1369 if (current != '') { r += '</tr></table>'; }
1370 r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt><span style=float:right>';
1371 r += getMeshActions(mesh, meshrights);
@@ -1575,7 +1602,7 @@
1602 if ((node.conn & 4) != 0) states.push('<span title="Intel&reg; AMT is routable.">Intel&reg; AMT</span>');
1603 if ((node.conn & 8) != 0) states.push('<span title="Mesh agent is reachable using another agent as relay.">Relay</span>');
1604 }
1578 - if ((node.pwr != undefined) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); }
1605 + if ((node.pwr != null) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); }
1606 return states.join(', ');
1607 }
1608
@@ -1660,7 +1687,7 @@
1687 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; }
1688 function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; }
1689 function deviceHostSort(a, b) { if (a.hostl > b.hostl) return 1; if (a.hostl < b.hostl) return -1; return 0; }
1663 - 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(); }
1690 + 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(); }
1691 function onSearchFocus(x) { searchFocus = x; }
1692 function onMapSearchFocus(x) { mapSearchFocus = x; }
1693 function onConsoleFocus(x) { consoleFocus = x; }
@@ -1668,8 +1695,8 @@
1695 var contextelement = null;
1696 function handleContextMenu(event) {
1697 hideContextMenu();
1671 - var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
1672 - var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
1698 + var scrollLeft = (window.pageXOffset !== null) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
1699 + var scrollTop = (window.pageYOffset !== null) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
1700 var elem = document.elementFromPoint(event.pageX - scrollLeft, event.pageY - scrollTop);
1701 if (elem && elem != null && elem.id == "MxMESH") {
1702 contextelement = elem;
@@ -1743,7 +1770,7 @@
1770 for (var i in nodes) {
1771 var loc = map_parseNodeLoc(nodes[i]);
1772 var feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
1746 - if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == undefined))) { // Draw markers for devices with locations
1773 + if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
1774 lat = loc[0];
1775 lon = loc[1];
1776 var type = loc[2];
@@ -1848,7 +1875,7 @@
1875 var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
1876 if (feature) {
1877 var nodeid = feature.getId();
1851 - if (nodeid != undefined) { gotoDevice(nodeid, 10); } // Goto general info tab
1878 + if (nodeid != null) { gotoDevice(nodeid, 10); } // Goto general info tab
1879 else { // For pointer
1880 var nodeFeatgoto = getCorrespondingFeature(feature); gotoDevice(nodeFeatgoto.getId(), 10);
1881 }
@@ -2149,7 +2176,7 @@
2176 function updatePlaceNodeTable(inputSearch) {
2177 var elements = document.getElementsByName("PlaceMapDeviceCheckbox"), count = 0;
2178 for (var i in nodes) {
2152 - var visible = ((nodes[i].namel.indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != undefined && nodes[i].hostl.indexOf(inputSearch) >= 0));
2179 + var visible = ((nodes[i].namel.indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != null && nodes[i].hostl.indexOf(inputSearch) >= 0));
2180 if (visible) { count++; }
2181 QV(nodes[i]._id + '-rowid', visible);
2182 }
@@ -2157,7 +2184,7 @@
2184 /*
2185 console.log(selected);
2186 for (var i in nodes) {
2160 - if ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != undefined && nodes[i].hostl.toLowerCase().indexOf(inputSearch) >= 0)) {
2187 + if ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != null && nodes[i].hostl.toLowerCase().indexOf(inputSearch) >= 0)) {
2188 console.log(selected.indexOf(nodes[i]._id));
2189 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':'') + ' />';
2190 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 @@
2411
2412 // Attribute: Mesh Agent
2413 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' ];
2387 - if ((node.agent != undefined) && (node.agent.id != undefined) && (node.agent.ver != undefined)) {
2414 + if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) {
2415 var str = '';
2416 if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; }
2417 if (node.agent.ver != 0) { str += ' v' + node.agent.ver; }
@@ -2392,12 +2419,12 @@
2419 }
2420
2421 // Attribute: Intel AMT
2395 - if (node.intelamt != undefined) {
2422 + if (node.intelamt != null) {
2423 var str = '';
2424 var provisioningStates = { 0: 'Not Activated (Pre)', 1: 'Not Activated (In)', 2: 'Activated' };
2398 - 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); }
2425 + 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); }
2426 if (node.intelamt.tls == 1) { str += ', TLS'; }
2400 - if (node.intelamt.user == undefined || node.intelamt.user == '') {
2427 + if (node.intelamt.user == null || node.intelamt.user == '') {
2428 if ((meshrights & 4) != 0) {
2429 str += ', <i style=color:#FF0000;cursor:pointer title="Edit Intel&reg; AMT credentials" onclick=editDeviceAmtSettings("' + node._id + '")>No Credentials</i>';
2430 } else {
@@ -2412,7 +2439,7 @@
2439 }
2440
2441 // Attribute: Mesh Agent Tag
2415 - if ((node.agent != undefined) && (node.agent.tag != undefined)) {
2442 + if ((node.agent != null) && (node.agent.tag != null)) {
2443 x += addDeviceAttribute('Agent Tag', node.agent.tag);
2444 }
2445
@@ -2481,13 +2508,13 @@
2508 // Show or hide the tabs
2509 // mesh.mtype: 1 = Intel AMT only, 2 = Mesh Agent
2510 // node.agent.caps (bitmask): 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console
2484 - QV('MainDevDesktop', (mesh.mtype == 1) || (node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 1) != 0));
2485 - QV('MainDevTerminal', (mesh.mtype == 1) || (node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 2) != 0));
2486 - QV('MainDevFiles', (mesh.mtype == 2) && ((node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 4) != 0)));
2487 - QV('MainDevAmt', node.intelamt != undefined);
2488 - QV('MainDevConsole', consoleRights && (mesh.mtype == 2) && ((node.agent == undefined) || (node.agent.caps == undefined) || ((node.agent.caps & 8) != 0)));
2489 - QV('p15uploadCore', (node.agent != undefined) && (node.agent.caps != undefined) && ((node.agent.caps & 16) != 0));
2490 - QH('p15coreName', ((node.agent != undefined) && (node.agent.core != undefined))?node.agent.core:'');
2511 + QV('MainDevDesktop', (mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0));
2512 + QV('MainDevTerminal', (mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0));
2513 + QV('MainDevFiles', (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0)));
2514 + QV('MainDevAmt', node.intelamt != null);
2515 + QV('MainDevConsole', consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0)));
2516 + QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
2517 + QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
2518
2519 // Setup/Refresh Intel AMT tab
2520 var amtFrameNode = Q('p14iframe').contentWindow.getCurrentMeshNode();
@@ -2534,7 +2561,7 @@
2561 // Called when MeshCommander needs new credentials or updated credentials.
2562 function updateAmtCredentials(forceDialog) {
2563 var node = getNodeFromId(desktopNode._id);
2537 - if ((forceDialog == true) || (node.intelamt.user == undefined) || (node.intelamt.user == '')) {
2564 + if ((forceDialog == true) || (node.intelamt.user == null) || (node.intelamt.user == '')) {
2565 editDeviceAmtSettings(desktopNode._id, updateAmtCredentialsEx);
2566 } else {
2567 Q('p14iframe').contentWindow.connectButtonfunctionEx();
@@ -2619,9 +2646,9 @@
2646 x += addHtmlValue('Username', '<input id=dp10username style=width:230px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
2647 x += addHtmlValue('Password', '<input id=dp10password type=password style=width:230px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
2648 x += addHtmlValue('Security', '<select id=dp10tls style=width:236px><option value=0>No TLS security</option><option value=1>TLS security required</option></select>');
2622 - if ((node.intelamt.user != undefined) && (node.intelamt.user != '')) { buttons = 7; }
2649 + if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; }
2650 setDialogMode(2, "Edit Intel&reg; AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func });
2624 - if ((node.intelamt.user != undefined) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
2651 + if ((node.intelamt.user != null) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
2652 Q('dp10tls').value = node.intelamt.tls;
2653 validateDeviceAmtSettings();
2654 }
@@ -2783,7 +2810,7 @@
2810 var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:230px maxlength=32 onchange=p10editdevicevalueValidate(' + mode + ') onkeyup=p10editdevicevalueValidate(' + mode + ') />');
2811 setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode);
2812 var v = currentNode[showEditNodeValueDialog_modes2[mode]];
2786 - if (v == undefined) v = '';
2813 + if (v == null) v = '';
2814 Q('dp10devicevalue').value = v;
2815 p10editdevicevalueValidate();
2816 }
@@ -2805,7 +2832,7 @@
2832 var desktopNode;
2833 function setupDesktop() {
2834 // Setup the remote desktop
2808 - if ((desktopNode != currentNode) && (desktop != undefined)) { desktop.Stop(); delete desktop; desktop = undefined; }
2835 + if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); delete desktop; desktop = null; }
2836 desktopNode = currentNode;
2837 updateDesktopButtons();
2838
@@ -2816,15 +2843,15 @@
2843 // Show and enable the right buttons
2844 function updateDesktopButtons() {
2845 var mesh = meshes[desktopNode.meshid];
2819 - var deskState = ((desktop != undefined) && (desktop.state != 0));
2846 + var deskState = ((desktop != null) && (desktop.state != 0));
2847
2848 // Show the right buttons
2849 QV('disconnectbutton1span', (deskState == true));
2850 QV('connectbutton1span', (deskState == false) && (mesh.mtype == 2));
2824 - QV('connectbutton1hspan', (deskState == false) && (desktopNode.intelamt != undefined && ((desktopNode.intelamt.ver != undefined) || (mesh.mtype == 1))));
2851 + QV('connectbutton1hspan', (deskState == false) && (desktopNode.intelamt != null && ((desktopNode.intelamt.ver != null) || (mesh.mtype == 1))));
2852
2853 // Show the right settings
2827 - QV('d7amtkvm', (desktopNode.intelamt != undefined && ((desktopNode.intelamt.ver != undefined) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
2854 + QV('d7amtkvm', (desktopNode.intelamt != null && ((desktopNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
2855 QV('d7meshkvm', (mesh.mtype == 2) && ((deskState == false) || (desktop.contype == 1)));
2856
2857 // Enable buttons
@@ -2839,10 +2866,10 @@
2866 function autoConnectDesktop(e) { if (autoConnectDesktopTimer == null) { autoConnectDesktopTimer = setInterval(connectDesktop, 100); } else { clearInterval(autoConnectDesktopTimer); autoConnectDesktopTimer = null; } }
2867
2868 function connectDesktop(e, contype) {
2842 - if (desktop == undefined) {
2869 + if (desktop == null) {
2870 if (contype == 2) {
2871 // Setup the Intel AMT remote desktop
2845 - if ((desktopNode.intelamt.user == undefined) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
2872 + if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
2873 desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'));
2874 desktop.onStateChanged = onDesktopStateChange;
2875 desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
@@ -2865,7 +2892,7 @@
2892 // Disconnect and clean up the remote desktop
2893 desktop.Stop();
2894 delete desktop;
2868 - desktop = undefined;
2895 + desktop = null;
2896 }
2897 }
2898
@@ -2874,14 +2901,14 @@
2901 if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
2902 QH('deskstatus', StatusStrs[xstate]);
2903 QE('deskSaveBtn', state == 3);
2877 - QV('deskFocusBtn', (desktop != undefined) && (desktop.contype == 2) && (state != 0) && (desktopsettings.showfocus));
2904 + QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (state != 0) && (desktopsettings.showfocus));
2905 QE('DeskCAD', state == 3);
2906 switch (state) {
2907 case 0:
2908 // Disconnect and clean up the remote desktop
2909 desktop.Stop();
2910 delete desktop;
2884 - desktop = undefined;
2911 + desktop = null;
2912 QV('DeskFocus', false);
2913 deskFocusBtn.value = 'All Focus';
2914 if (fullscreen == true) { deskToggleFull(); }
@@ -2923,7 +2950,7 @@
2950 d7showcursor.checked = desktopsettings.showmouse;
2951 d7bitmapquality.value = desktopsettings.quality;
2952 d7bitmapscaling.value = desktopsettings.scaling;
2926 - QV('deskFocusBtn', (desktop != undefined) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
2953 + QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
2954 }
2955
2956 var fullscreen = false;
@@ -2988,22 +3015,22 @@
3015
3016 // Send CTRL-ALT-DEL
3017 function sendCAD() {
2991 - if (xxdialogMode || desktop == undefined || desktop.State != 3) return;
3018 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
3019 desktop.m.sendcad();
3020 }
3021
3022 // Save the desktop image to file
3023 function deskSaveImage() {
2997 - if (xxdialogMode || desktop == undefined || desktop.State != 3) return;
3024 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
3025 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);
3026 Q("Desk")['toBlob'](function (blob) { saveAs(blob, n + ".jpg"); });
3027 }
3028
3002 - function dmousedown(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mousedown(e) }
3003 - function dmouseup(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mouseup(e) }
3004 - function dmousemove(e) { if (!xxdialogMode && desktop != undefined) desktop.m.mousemove(e) }
3005 - function dmousewheel(e) { if (!xxdialogMode && desktop != undefined) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
3006 - function drotate(x) { if (!xxdialogMode && desktop != undefined) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
3029 + function dmousedown(e) { if (!xxdialogMode && desktop != null) desktop.m.mousedown(e) }
3030 + function dmouseup(e) { if (!xxdialogMode && desktop != null) desktop.m.mouseup(e) }
3031 + function dmousemove(e) { if (!xxdialogMode && desktop != null) desktop.m.mousemove(e) }
3032 + function dmousewheel(e) { if (!xxdialogMode && desktop != null) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
3033 + function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
3034
3035 //
3036 // TERMINAL
@@ -3012,7 +3039,7 @@
3039 var terminalNode;
3040 function setupTerminal() {
3041 // Setup the terminal
3015 - if ((terminalNode != currentNode) && (terminal != undefined)) { terminal.Stop(); delete terminal; terminal = undefined; }
3042 + if ((terminalNode != currentNode) && (terminal != null)) { terminal.Stop(); delete terminal; terminal = null; }
3043 terminalNode = currentNode;
3044 updateTerminalButtons();
3045 }
@@ -3020,12 +3047,12 @@
3047 // Show and enable the right buttons
3048 function updateTerminalButtons() {
3049 var mesh = meshes[terminalNode.meshid];
3023 - var termState = ((terminal != undefined) && (terminal.state != 0));
3050 + var termState = ((terminal != null) && (terminal.state != 0));
3051
3052 // Show the right buttons
3053 QV('disconnectbutton2span', (termState == true));
3054 QV('connectbutton2span', (termState == false) && (mesh.mtype == 2));
3028 - QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != undefined && ((terminalNode.intelamt.ver != undefined) || (mesh.mtype == 1))));
3055 + QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != null && ((terminalNode.intelamt.ver != null) || (mesh.mtype == 1))));
3056
3057 // Enable buttons
3058 var online = ((terminalNode.conn & 1) != 0); // If Agent (1) connected, enable Terminal
@@ -3052,10 +3079,10 @@
3079 // Disconnected, clear the terminal
3080 xterminal.m.TermResetScreen();
3081 xterminal.m.TermDraw();
3055 - if (terminal != undefined) {
3082 + if (terminal != null) {
3083 terminal.Stop();
3084 delete terminal;
3058 - terminal = undefined;
3085 + terminal = null;
3086 }
3087 break;
3088 case 3:
@@ -3072,7 +3099,7 @@
3099 if (!terminal) {
3100 if (contype == 2) {
3101 // Setup the Intel AMT terminal
3075 - if ((terminalNode.intelamt.user == undefined) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal); return; }
3102 + if ((terminalNode.intelamt.user == null) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal); return; }
3103 terminal = CreateAmtRedirect(CreateAmtRemoteTerminal('Term'));
3104 terminal.onStateChanged = onTerminalStateChange;
3105 terminal.Start(terminalNode._id, 16994, '*', '*', 0);
@@ -3089,7 +3116,7 @@
3116 //QH('Term', '');
3117 terminal.Stop();
3118 delete terminal;
3092 - terminal = undefined;
3119 + terminal = null;
3120 }
3121 Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses.
3122 }
@@ -3146,7 +3173,7 @@
3173 filesNode = currentNode;
3174 var online = ((filesNode.conn & 1) != 0)?true:false; // If Agent (1) connected, enable Terminal
3175 QE('p13Connect', online);
3149 - if (((samenode == false) || (online == false)) && files) { files.Stop(); delete files; files = undefined; }
3176 + if (((samenode == false) || (online == false)) && files) { files.Stop(); delete files; files = null; }
3177 }
3178
3179 function onFilesStateChange(xfiles, state) {
@@ -3161,7 +3188,7 @@
3188 QH('p13currentpath', '');
3189 QE('p13FolderUp', false);
3190 p13setActions();
3164 - if (files != undefined) { files.Stop(); delete files; files = undefined; }
3191 + if (files != null) { files.Stop(); delete files; files = null; }
3192 break;
3193 case 3:
3194 files.Send(JSON.stringify({ action: 'ls', reqid: 1, path: '' }));
@@ -3192,7 +3219,7 @@
3219 //QH('Term', '');
3220 files.Stop();
3221 delete files;
3195 - files = undefined;
3222 + files = null;
3223 }
3224 }
3225
@@ -3249,11 +3276,11 @@
3276
3277 // Figure out the date
3278 var fdatestr = '';
3252 - if (f.d != undefined) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
3279 + if (f.d != null) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
3280
3281 // Figure out the size
3282 var fsize = '';
3256 - if (f.s != undefined) { fsize = getFileSizeStr(f.s); }
3283 + if (f.s != null) { fsize = getFileSizeStr(f.s); }
3284
3285 var h = '';
3286 if (f.t < 3) {
@@ -3274,7 +3301,7 @@
3301 QE('p13FolderUp', p13filetreelocation.length != 0);
3302
3303 // Re-check all boxes if needed using names
3277 - if (checkedNames != undefined) {
3304 + if (checkedNames != null) {
3305 checkedBoxes = [];
3306 checkboxes = document.getElementsByName('fd');
3307 for (var i in filetreexx) { if (checkedNames.indexOf(filetreexx[i].n) >= 0) { checkedBoxes.push(filetreexx[i].nx); } }
@@ -3295,7 +3322,7 @@
3322 }
3323
3324 function p13folderup(x) {
3298 - if (x == undefined) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
3325 + if (x == null) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
3326 files.Send(JSON.stringify({ action: 'ls', reqid: 1, path: p13filetreelocation.join('/') }));
3327 }
3328
@@ -3306,7 +3333,7 @@
3333
3334 function p13sort_files(files) {
3335 var r = [], sortselection = Q('p13sortdropdown').value;
3309 - 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]); }
3336 + 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]); }
3337 p13sortorder = 1;
3338 if (sortselection > 3) { p13sortorder = -1; sortselection -= 3; }
3339 if (sortselection == 1) { r.sort(p13sort_filename); }
@@ -3402,7 +3429,7 @@
3429 //console.log('p13downloadFileCancel');
3430 downloadFile.Stop();
3431 delete downloadFile;
3405 - downloadFile = undefined;
3432 + downloadFile = null;
3433 }
3434
3435 // Called by the file transport to indicate when the transport connection state has changed
@@ -3410,7 +3437,7 @@
3437 switch (state) {
3438 case 0: // Transport as disconnected. If this is not part of an abort, we need to save the file
3439 setDialogMode(0); // Close any dialog boxes if present
3413 - if ((downloadFile != undefined) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file
3440 + if ((downloadFile != null) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file
3441 break;
3442 case 3: // Transport as connected, send a command to indicate we want to start a file download
3443 downloadFile.Send(JSON.stringify({ action: 'download', reqid: 1, path: downloadFile.xpath }));
@@ -3492,12 +3519,12 @@
3519
3520 // Used to cancel the entire transfer.
3521 function p13uploadFileCancel(button, tag) {
3495 - if (uploadFile != undefined) {
3496 - if (uploadFile.ws != undefined) {
3522 + if (uploadFile != null) {
3523 + if (uploadFile.ws != null) {
3524 uploadFile.ws.Stop();
3498 - uploadFile.ws = undefined;
3525 + uploadFile.ws = null;
3526 }
3500 - uploadFile = undefined;
3527 + uploadFile = null;
3528 }
3529 setDialogMode(0); // Close any dialog boxes if present
3530 }
@@ -3505,7 +3532,7 @@
3532 // Receive upload ack from the mesh agent, use this to keep sending more data
3533 function p13gotUploadData(data) {
3534 var cmd = JSON.parse(data);
3508 - if ((uploadFile == undefined) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
3535 + if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
3536
3537 if (cmd.action == 'uploadstart') {
3538 p13uploadNextPart(false);
@@ -3524,7 +3551,7 @@
3551 var end = uploadFile.xptr + 4096;
3552 if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
3553 if (start == data.byteLength) {
3527 - if (uploadFile.ws != undefined) { uploadFile.ws.Stop(); uploadFile.ws = undefined; }
3554 + if (uploadFile.ws != null) { uploadFile.ws.Stop(); uploadFile.ws = null; }
3555 if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadReconnect(); } else { p13uploadFileCancel(); }
3556 } else {
3557 var datapart = data.slice(start, end);
@@ -3567,7 +3594,7 @@
3594 var mesh = meshes[consoleNode.meshid];
3595 var meshrights = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
3596 if ((meshrights & 16) != 0) {
3570 - if (consoleNode.consoleText == undefined) { consoleNode.consoleText = ''; }
3597 + if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
3598 if (samenode == false) {
3599 QH('p15agentConsole', consoleNode.consoleText);
3600 Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
@@ -3618,7 +3645,7 @@
3645 // Handle Mesh Agent console data
3646 function p15consoleReceive(node, data) {
3647 data = '<div>' + EscapeHtmlBreaks(data) + '</div>'
3621 - if (node.consoleText == undefined) { node.consoleText = data; } else { node.consoleText += data; }
3648 + if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
3649 if (consoleNode == node) {
3650 Q('p15agentConsole').innerHTML += data;
3651 Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
@@ -4032,7 +4059,7 @@
4059
4060 filetreelinkpath = '';
4061 for (var i in filetreelocation) {
4035 - if ((filetreex.f != undefined) && (filetreex.f[filetreelocation[i]] != undefined)) {
4062 + if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) {
4063 filetreelocation2.push(filetreelocation[i]);
4064 fullPath += ' / ' + filetreelocation[i];
4065 if ((folderdepth == 1)) {
@@ -4044,7 +4071,7 @@
4071 if (filetreelinkpath != '') { filetreelinkpath += '/' + filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + filetreelocation[i]; } }
4072 }
4073 filetreex = filetreex.f[filetreelocation[i]];
4047 - displayPath += ' / <a style=cursor:pointer onclick=p5folderup(' + folderdepth + ')>' + (filetreex.n != undefined?filetreex.n:filetreelocation[i]) + '</a>';
4074 + displayPath += ' / <a style=cursor:pointer onclick=p5folderup(' + folderdepth + ')>' + (filetreex.n != null?filetreex.n:filetreelocation[i]) + '</a>';
4075 folderdepth++;
4076 } else {
4077 break;
@@ -4066,11 +4093,11 @@
4093
4094 // Figure out the date
4095 var fdatestr = '';
4069 - if (f.d != undefined) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
4096 + if (f.d != null) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + "/" + (fdate.getDate()) + "/" + fdate.getFullYear() + " " + fdate.toLocaleTimeString() + "&nbsp;"; }
4097
4098 // Figure out the size
4099 var fsize = '';
4073 - if (f.s != undefined) { fsize = getFileSizeStr(f.s); }
4100 + if (f.s != null) { fsize = getFileSizeStr(f.s); }
4101
4102 var h = '';
4103 if (f.t < 3) {
@@ -4087,7 +4114,7 @@
4114 if (f.t < 3) { html1 += h; } else { html2 += h; }
4115 }
4116
4090 - //if (f.parent == undefined) { }
4117 + //if (f.parent == null) { }
4118 QH('p5rightOfButtons', p5getQuotabar(filetreex));
4119
4120 QH('p5files', html1 + html2);
@@ -4108,7 +4135,7 @@
4135
4136 function p5getQuotabar(f) {
4137 while (f.t > 1) { f = f.parent; }
4111 - if ((f.t != 1) || (f.maxbytes == undefined)) return '';
4138 + if ((f.t != 1) || (f.maxbytes == null)) return '';
4139 var tf = Math.floor(f.s / 1024), tq = Math.floor((f.maxbytes - f.s) / 1024);
4140 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>';
4141 }
@@ -4122,7 +4149,7 @@
4149
4150 function p5sort_files(files) {
4151 var r = [], sortselection = Q('p5sortdropdown').value;
4125 - 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]); }
4152 + 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]); }
4153 sortorder = 1;
4154 if (sortselection > 3) { sortorder = -1; sortselection -= 3; }
4155 if (sortselection == 1) { r.sort(p5sort_filename); }
@@ -4144,9 +4171,9 @@
4171 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; }
4172 function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; }
4173 function p5selectallfile() { var nv = (getFileSelCount() == 0), checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p5setActions(); }
4147 - 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; }
4174 + 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; }
4175 function getFileSizeStr(size) { if (size == 1) return "1 byte"; return "" + size + " bytes"; }
4149 - function p5folderup(x) { if (x == undefined) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); }
4176 + function p5folderup(x) { if (x == null) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); }
4177 function p5folderset(x) { filetreelocation.push(decodeURIComponent(x)); updateFiles(); }
4178 function p5createfolder() { setDialogMode(2, "New Folder", 3, p5createfolderEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck() style=width:100% />'); Q('p5renameinput').focus(); }
4179 function p5createfolderEx() { meshserver.Send({ action: 'fileoperation', fileop: 'createfolder', path: filetreelocation, newfolder: Q('p5renameinput').value}); }
@@ -4277,7 +4304,7 @@
4304 }
4305 if (msg != '') msg += ', ';
4306 if (user.name != userinfo.name) { msg += "<a onclick=showUserAdminDialog(event,\"" + user._id + "\")>"; }
4280 - if ((user.siteadmin == undefined) || (user.siteadmin == 0)) {
4307 + if ((user.siteadmin == null) || (user.siteadmin == 0)) {
4308 msg += "User";
4309 } else if (user.siteadmin == 8) {
4310 msg += "User with server files";
@@ -4286,7 +4313,7 @@
4313 } else {
4314 msg += "Partial Admin";
4315 }
4289 - if ((user.quota != undefined) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
4316 + if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
4317 if (user.name != userinfo.name) { msg += "</a>"; }
4318 if (user.email != null) {
4319 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 @@
4397 QE('ua_serverrestore', userinfo.siteadmin == 0xFFFFFFFF);
4398 QE('ua_fileaccess', userinfo.siteadmin == 0xFFFFFFFF);
4399 QE('ua_serverupdate', userinfo.siteadmin == 0xFFFFFFFF);
4373 - Q('ua_fileaccessquota').value = (user.quota != undefined)?(user.quota / 1024):'';
4400 + Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):'';
4401 showUserAdminDialogValidate();
4402 return false;
4403 }
@@ -4429,7 +4456,7 @@
4456
4457 d3filetreelinkpath = '';
4458 for (var i in d3filetreelocation) {
4432 - if ((filetreex.f != undefined) && (filetreex.f[d3filetreelocation[i]] != undefined)) {
4459 + if ((filetreex.f != null) && (filetreex.f[d3filetreelocation[i]] != null)) {
4460 d3filetreelocation2.push(d3filetreelocation[i]);
4461 if ((folderdepth == 1)) {
4462 var sp = d3filetreelocation[i].split('/');
@@ -4459,7 +4486,7 @@
4486
4487 // Figure out the size
4488 var fsize = '';
4462 - if (f.s != undefined) { fsize = getFileSizeStr(f.s); }
4489 + if (f.s != null) { fsize = getFileSizeStr(f.s); }
4490
4491 var h = '';
4492 if (f.t < 3) {
@@ -4480,7 +4507,7 @@
4507 }
4508
4509 function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); }
4483 - function d3folderup(x) { if (x == undefined) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
4510 + function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
4511 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; }
4512 function d3setActions() {
4513 var mode = Q('d3uploadMode').value;
@@ -4523,7 +4550,7 @@
4550 var t = '';
4551 var d = new Date(n.time);
4552 var icon = 0;
4526 - if (n.nodeid != undefined) {
4553 + if (n.nodeid != null) {
4554 var node = getNodeFromId(n.nodeid);
4555 if (node != null) {
4556 //console.log(node);
@@ -4546,7 +4573,7 @@
4573 for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
4574 if (j != -1) {
4575 var n = notifications[j];
4549 - if (n.nodeid != undefined) {
4576 + if (n.nodeid != null) {
4577 if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
4578 else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
4579 else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
@@ -4578,8 +4605,8 @@
4605
4606 // Add a new notification and play the notification sound
4607 function addNotification(n) {
4581 - if (n.time == undefined) { n.time = Date.now(); }
4582 - if (n.id == undefined) { n.id = Math.random(); }
4608 + if (n.time == null) { n.time = Date.now(); }
4609 + if (n.id == null) { n.id = Math.random(); }
4610 notifications.unshift(n);
4611 setNotificationCount(notifications.length);
4612 Q('chimes').play();
@@ -4598,7 +4625,7 @@
4625 // POPUP DIALOG
4626 //
4627
4601 - // undefined = Hidden, 1 = Generic Message
4628 + // null = Hidden, 1 = Generic Message
4629 var xxdialogMode;
4630 var xxdialogFunc;
4631 var xxdialogButtons;
@@ -4662,8 +4689,8 @@
4689
4690 // Generic methods
4691 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('/'); }
4665 - function putstore(name, val) { try { if (typeof (localStorage) === "undefined") return; localStorage.setItem(name, val); } catch (e) { } }
4666 - 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; } }
4692 + function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } }
4693 + 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; } }
4694 function addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>&diams; " + x + "</a>"; }
4695 function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
4696 function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
webserver.js
+23 -42
@@ -271,11 +271,11 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
271 obj.authenticate(req.body.username, req.body.password, domain, function (err, userid, passhint) {
272 if (userid) {
273 var user = obj.users[userid];
274 -
274 +
275 // Save login time
276 user.login = Date.now();
277 obj.db.SetUser(user);
278 -
278 +
279 // Regenerate session when signing in to prevent fixation
280 req.session.regenerate(function () {
281 // 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
302 res.redirect(domain.url);
303 }
304 });
305 -
305 +
306 obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id })
307 } else {
308 delete req.session.loginmode;
@@ -407,7 +407,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
407 var domain = checkUserIpAddress(req, res);
408 if (domain == null) return;
409 if (req.query.c != null) {
410 - var cookie = obj.decodeCookie(req.query.c, null, 30);
410 + var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30);
411 if ((cookie != null) && (cookie.u != null) && (cookie.e != null)) {
412 var idsplit = cookie.u.split('/');
413 if ((idsplit.length != 2) || (idsplit[0] != domain.id)) {
@@ -444,10 +444,13 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
444 delete userinfo.domain;
445 delete userinfo.subscriptions;
446 delete userinfo.passtype;
447 - 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 })
447 + 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 })
448
449 // Send the confirmation page
450 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>.' });
451 +
452 + // Send a notification
453 + obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified: <b>' + EscapeHtml(userinfo.email) + '</b>.' , nolog: 1 })
454 }
455 });
456 }
@@ -571,6 +574,15 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
574 req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
575 req.session.domainid = domain.id;
576 req.session.currentNode = '';
577 + } else if (req.query.login && (obj.parent.loginCookieEncryptionKey != null)) {
578 + var loginCookie = obj.parent.decodeCookie(req.query.login, obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
579 + if ((loginCookie != null) && (loginCookie.a == 3) && (loginCookie.u != null) && (loginCookie.u.split('/')[1] == domain.id)) {
580 + // If a login cookie was provided, setup the session here.
581 + if (req.session && req.session.loginmode) { delete req.session.loginmode; }
582 + req.session.userid = loginCookie.u;
583 + req.session.domainid = domain.id;
584 + req.session.currentNode = '';
585 + }
586 }
587 // If a user is logged in, serve the default app, otherwise server the login app.
588 if (req.session && req.session.userid) {
@@ -579,11 +591,15 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
591 if (req.session.viewmode) {
592 viewmode = req.session.viewmode;
593 delete req.session.viewmode;
594 + } else if (req.query.viewmode) {
595 + viewmode = req.query.viewmode;
596 }
597 var currentNode = '';
598 if (req.session.currentNode) {
599 currentNode = req.session.currentNode;
600 delete req.session.currentNode;
601 + } else if (req.query.node) {
602 + currentNode = 'node/' + domain.id + '/' + req.query.node;
603 }
604 var user;
605 var logoutcontrol;
@@ -894,7 +910,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
910 } else {
911 // Get the session from the cookie
912 if (obj.parent.multiServer == null) { return; }
897 - var session = obj.decodeCookie(req.query.auth);
913 + var session = obj.parent.decodeCookie(req.query.auth);
914 if (session == null) { console.log('ERR: Invalid cookie'); return; }
915 if (session.domainid != domain.id) { console.log('ERR: Invalid domain'); return; }
916 user = obj.users[session.userid];
@@ -1484,7 +1500,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1500 }
1501
1502 // Force mesh agent disconnection
1487 - function forceMeshAgentDisconnect(user, domain, nodeid, disconnectMode) {
1503 + obj.forceMeshAgentDisconnect = function(user, domain, nodeid, disconnectMode) {
1504 if (nodeid == null) return;
1505 var splitnode = nodeid.split('/');
1506 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
1658 }
1659 }
1660
1645 - // Generate a cryptographic key used to encode and decode cookies
1646 - obj.generateCookieKey = function () {
1647 - return new Buffer(obj.crypto.randomBytes(32), 'binary');
1648 - //return Buffer.alloc(32, 0); // Sets the key to zeros, debug only.
1649 - }
1650 -
1651 - // Encode an object as a cookie using a key. (key must be 32 bytes long)
1652 - obj.encodeCookie = function (o, key) {
1653 - try {
1654 - if (key == null) { key = obj.serverKey; }
1655 - o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time
1656 - var iv = new Buffer(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key, iv);
1657 - var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]);
1658 - return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
1659 - } catch (e) { return null; }
1660 - }
1661 -
1662 - // 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)
1663 - obj.decodeCookie = function (cookie, key, timeout) {
1664 - try {
1665 - if (key == null) { key = obj.serverKey; }
1666 - cookie = new Buffer(cookie.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64');
1667 - var decipher = obj.crypto.createDecipheriv('aes-256-gcm', key, cookie.slice(0, 12));
1668 - decipher.setAuthTag(cookie.slice(12, 16));
1669 - var o = JSON.parse(decipher.update(cookie.slice(28), 'binary', 'utf8') + decipher.final('utf8'));
1670 - if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { return null; }
1671 - o.time = o.time * 1000; // Decode the cookie creation time
1672 - o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
1673 - if (timeout == null) { timeout = 2; }
1674 - 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)
1675 - return o;
1676 - } catch (e) { return null; }
1677 - }
1678 -
1679 - obj.serverKey = obj.generateCookieKey();
1661 return obj;
1662 }