From 53aacd87c8dc55af3c3ab66d99517885de6a2d00 Mon Sep 17 00:00:00 2001 From: Bryan Roe Date: Tue, 29 Oct 2019 11:05:33 -0700 Subject: [PATCH 1/9] Updated safemode text --- agents/meshcore.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/agents/meshcore.js b/agents/meshcore.js index c02bbdea..1bed88fc 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -1815,6 +1815,19 @@ function createMeshCore(agent) { break; case 'STATUS': var nextboot = require('win-bcd').getKey('safeboot'); + if (nextboot) + { + switch(nextboot) + { + case 'Network': + case 'network': + nextboot = 'SAFE_MODE_NETWORK'; + break; + default: + nextboot = 'SAFE_MODE'; + break; + } + } response = 'Current: ' + require('win-bcd').bootMode + ' , NextBoot: ' + (nextboot ? nextboot : 'NORMAL'); break; } From 6f382affdef258b2f536861c47abdc3df76fa9b7 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 29 Oct 2019 11:10:57 -0700 Subject: [PATCH 2/9] Added correct hostname header when loading reverse proxy certificate. --- certoperations.js | 14 ++++++++------ meshcentral.js | 8 +++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/certoperations.js b/certoperations.js index 3cccfd53..6cbcbbc5 100644 --- a/certoperations.js +++ b/certoperations.js @@ -195,25 +195,27 @@ module.exports.CertificateOperations = function (parent) { } // Return the certificate of the remote HTTPS server - obj.loadCertificate = function (url, tag, func) { + obj.loadCertificate = function (url, hostname, tag, func) { + console.log('loadCertificate', url, hostname); const u = require('url').parse(url); if (u.protocol == 'https:') { // Read the certificate from HTTPS - const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: u.hostname, rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); }); + if (hostname == null) { hostname = u.hostname; } + const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: hostname, rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); }); tlssocket.xxurl = url; tlssocket.xxfunc = func; tlssocket.xxtag = tag; - tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert.raw.toString('binary'), this.xxtag); }); - tlssocket.on('error', function () { this.xxfunc(this.xxurl, null, this.xxtag); }); + tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert.raw.toString('binary'), hostname, this.xxtag); }); + tlssocket.on('error', function () { this.xxfunc(this.xxurl, null, hostname, this.xxtag); }); } else if (u.protocol == 'file:') { // Read the certificate from a file obj.fs.readFile(url.substring(7), 'utf8', function (err, data) { if (err) { func(url, null, tag); return; } var x1 = data.indexOf('-----BEGIN CERTIFICATE-----'), x2 = data.indexOf('-----END CERTIFICATE-----'); if ((x1 >= 0) && (x2 > x1)) { - func(url, Buffer.from(data.substring(x1 + 27, x2), 'base64').toString('binary'), tag); + func(url, Buffer.from(data.substring(x1 + 27, x2), 'base64').toString('binary'), hostname, tag); } else { - func(url, data, tag); + func(url, data, hostname, tag); } }); } else { func(url, null, tag); } diff --git a/meshcentral.js b/meshcentral.js index f47b9d92..07252a3b 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -862,7 +862,9 @@ function CreateMeshCentralServer(config, args) { // Load web certs webCertLoadCount++; - obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, obj.config.domains[i], function (url, cert, xdomain) { + var dnsname = obj.config.domains[i].dns; + if ((dnsname == null) && (i == '') && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; } + obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, dnsname, obj.config.domains[i], function (url, cert, xhostname, xdomain) { if (cert != null) { // Hash the entire cert var hash = obj.crypto.createHash('sha384').update(Buffer.from(cert, 'binary')).digest('hex'); @@ -875,11 +877,11 @@ function CreateMeshCentralServer(config, args) { //console.log('V1: ' + xdomain.certkeyhash); } catch (ex) { } - console.log('Loaded web certificate from ' + url); + console.log('Loaded web certificate from \"' + url + '\", host: \"' + xhostname + '\"'); console.log(' SHA384 cert hash: ' + xdomain.certhash); if (xdomain.certhash != xdomain.certkeyhash) { console.log(' SHA384 key hash: ' + xdomain.certkeyhash); } } else { - console.log('Failed to load web certificate at: ' + url); + console.log('Failed to load web certificate at: \"' + url + '\", host: \"' + xhostname + '\"'); } webCertLoadCount--; if (webCertLoadCount == 0) { obj.StartEx4(); } // Done loading all certificates From 9e30079ed6e17a0d202fcc965b8786a400e9bac0 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 29 Oct 2019 11:14:51 -0700 Subject: [PATCH 3/9] Fixed reverse proxy cert load for sub-domains with no DNS setting. --- meshcentral.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meshcentral.js b/meshcentral.js index 07252a3b..81909bfe 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -863,7 +863,7 @@ function CreateMeshCentralServer(config, args) { // Load web certs webCertLoadCount++; var dnsname = obj.config.domains[i].dns; - if ((dnsname == null) && (i == '') && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; } + if ((dnsname == null) && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; } obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, dnsname, obj.config.domains[i], function (url, cert, xhostname, xdomain) { if (cert != null) { // Hash the entire cert diff --git a/package.json b/package.json index 83796b34..1094d208 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.3-l", + "version": "0.4.3-m", "keywords": [ "Remote Management", "Intel AMT", From bdd1a5b2e24c3f200d1dfdeefa95927f96ab9762 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 29 Oct 2019 14:05:58 -0700 Subject: [PATCH 4/9] Fixed what certificate name is used. --- certoperations.js | 11 +++++------ package.json | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/certoperations.js b/certoperations.js index 6cbcbbc5..b721d18e 100644 --- a/certoperations.js +++ b/certoperations.js @@ -497,13 +497,12 @@ module.exports.CertificateOperations = function (parent) { r.AmtMpsName = obj.pki.certificateFromPem(r.mps.cert).subject.getField("CN").value; var webCertificate = obj.pki.certificateFromPem(r.web.cert); r.WebIssuer = webCertificate.issuer.getField("CN").value; - r.CommonName = webCertificate.subject.getField("CN").value; - if (r.CommonName.startsWith('*.')) { - if (commonName.indexOf('.') == -1) { console.log("ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate."); process.exit(0); return; } - if (commonName.startsWith('*.')) { console.log("ERROR: Server can't use a wildcard name: " + commonName); process.exit(0); return; } - r.CommonName = commonName; + if (commonName == "un-configured") { // If the "cert" name is not set, try to use the certificate CN instead (ok if the certificate is not wildcard). + commonName = webCertificate.subject.getField("CN").value; + if (commonName.startsWith('*.')) { console.log("ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate."); process.exit(0); return; } } - r.CommonNames = [ r.CommonName.toLowerCase() ]; + r.CommonName = commonName; + r.CommonNames = [commonName.toLowerCase()]; var altNames = webCertificate.getExtension("subjectAltName"); if (altNames) { for (i = 0; i < altNames.altNames.length; i++) { diff --git a/package.json b/package.json index 1094d208..a2dd4bb8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.3-m", + "version": "0.4.3-o", "keywords": [ "Remote Management", "Intel AMT", From f062c98aad219d567a0877c31fbc71b0fecee067 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 29 Oct 2019 16:17:29 -0700 Subject: [PATCH 5/9] Improved support for reverse-proxy certificate loading. --- certoperations.js | 3 +- common.js | 3 + meshagent.js | 8 +++ meshcentral.js | 176 +++++++++++++++++++++++++++------------------- package.json | 2 +- webserver.js | 23 +++--- 6 files changed, 127 insertions(+), 88 deletions(-) diff --git a/certoperations.js b/certoperations.js index b721d18e..bc5b19c5 100644 --- a/certoperations.js +++ b/certoperations.js @@ -196,7 +196,6 @@ module.exports.CertificateOperations = function (parent) { // Return the certificate of the remote HTTPS server obj.loadCertificate = function (url, hostname, tag, func) { - console.log('loadCertificate', url, hostname); const u = require('url').parse(url); if (u.protocol == 'https:') { // Read the certificate from HTTPS @@ -218,7 +217,7 @@ module.exports.CertificateOperations = function (parent) { func(url, data, hostname, tag); } }); - } else { func(url, null, tag); } + } else { func(url, null, hostname, tag); } }; // Check if a configuration file exists diff --git a/common.js b/common.js index 511dc3c7..6ad59eaf 100644 --- a/common.js +++ b/common.js @@ -35,6 +35,9 @@ module.exports.makeFilename = function (v) { return v.split('\\').join('').split // Move an element from one position in an array to a new position module.exports.ArrayElementMove = function(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); }; +// Format a string with arguments, "replaces {0} and {1}..." +module.exports.format = function (format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); }; + // Print object for HTML module.exports.ObjectToStringEx = function (x, c) { var r = "", i; diff --git a/meshagent.js b/meshagent.js index f9dfdedb..62b781c1 100644 --- a/meshagent.js +++ b/meshagent.js @@ -53,6 +53,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1); } + // Remove this agent from the list of agents with bad web certificates + if (obj.badWebCert) { delete parent.wsagentsWithBadWebCerts[obj.badWebCert]; } + // Get the current mesh const mesh = parent.meshes[obj.dbMeshKey]; @@ -381,6 +384,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { } else { // Check that the server hash matches our own web certificate hash (SHA384) if ((getWebCertHash(domain) != msg.substring(2, 50)) && (getWebCertFullHash(domain) != msg.substring(2, 50))) { + if (parent.parent.supportsProxyCertificatesRequest !== false) { + obj.badWebCert = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); + parent.wsagentsWithBadWebCerts[obj.badWebCert] = obj; // Add this agent to the list of of agents with bad web certificates. + parent.parent.updateProxyCertificates(); + } parent.agentStats.agentBadWebCertHashCount++; console.log('Agent bad web cert hash (Agent:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (Buffer.from(getWebCertHash(domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').'); console.log('Agent reported web cert hash:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex')) + '.'); diff --git a/meshcentral.js b/meshcentral.js index 81909bfe..4ed6c506 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -396,34 +396,34 @@ function CreateMeshCentralServer(config, args) { // Show a list of all configuration files in the database if (obj.args.dblistconfigfiles) { - obj.db.GetAllType('cfile', function (err, docs) { if (err == null) { if (docs.length == 0) { console.log('No files found.'); } else { for (var i in docs) { console.log(docs[i]._id.split('/')[1] + ', ' + Buffer.from(docs[i].data, 'base64').length + ' bytes.'); } } } else { console.log('Unable to read from database.'); } process.exit(); }); return; + obj.db.GetAllType('cfile', function (err, docs) { if (err == null) { if (docs.length == 0) { console.log("No files found."); } else { for (var i in docs) { console.log(docs[i]._id.split('/')[1] + ', ' + Buffer.from(docs[i].data, 'base64').length + ' bytes.'); } } } else { console.log('Unable to read from database.'); } process.exit(); }); return; } // Display the content of a configuration file in the database if (obj.args.dbshowconfigfile) { - if (typeof obj.args.configkey != 'string') { console.log('Error, --configkey is required.'); process.exit(); return; } + if (typeof obj.args.configkey != 'string') { console.log("Error, --configkey is required."); process.exit(); return; } obj.db.getConfigFile(obj.args.dbshowconfigfile, function (err, docs) { if (err == null) { - if (docs.length == 0) { console.log('File not found.'); } else { + if (docs.length == 0) { console.log("File not found."); } else { var data = obj.db.decryptData(obj.args.configkey, docs[0].data); - if (data == null) { console.log('Invalid config key.'); } else { console.log(data); } + if (data == null) { console.log("Invalid config key."); } else { console.log(data); } } - } else { console.log('Unable to read from database.'); } + } else { console.log("Unable to read from database."); } process.exit(); }); return; } // Delete all configuration files from database if (obj.args.dbdeleteconfigfiles) { - console.log('Deleting all configuration files from the database...'); obj.db.RemoveAllOfType('cfile', function () { console.log('Done.'); process.exit(); }); + console.log("Deleting all configuration files from the database..."); obj.db.RemoveAllOfType('cfile', function () { console.log('Done.'); process.exit(); }); } // Push all relevent files from meshcentral-data into the database if (obj.args.dbpushconfigfiles) { - if (typeof obj.args.configkey != 'string') { console.log('Error, --configkey is required.'); process.exit(); return; } + if (typeof obj.args.configkey != 'string') { console.log("Error, --configkey is required."); process.exit(); return; } if ((obj.args.dbpushconfigfiles !== true) && (typeof obj.args.dbpushconfigfiles != 'string')) { - console.log('Usage: --dbpulldatafiles (path) This will import files from folder into the database'); - console.log(' --dbpulldatafiles This will import files from meshcentral-data into the db.'); + console.log("Usage: --dbpulldatafiles (path) This will import files from folder into the database"); + console.log(" --dbpulldatafiles This will import files from meshcentral-data into the db."); process.exit(); } else { if ((obj.args.dbpushconfigfiles == '*') || (obj.args.dbpushconfigfiles === true)) { obj.args.dbpushconfigfiles = obj.datapath; } @@ -454,20 +454,20 @@ function CreateMeshCentralServer(config, args) { // Pull all database files into meshcentral-data if (obj.args.dbpullconfigfiles) { - if (typeof obj.args.configkey != 'string') { console.log('Error, --configkey is required.'); process.exit(); return; } + if (typeof obj.args.configkey != 'string') { console.log("Error, --configkey is required."); process.exit(); return; } if (typeof obj.args.dbpullconfigfiles != 'string') { - console.log('Usage: --dbpulldatafiles (path)'); + console.log("Usage: --dbpulldatafiles (path)"); process.exit(); } else { obj.db.GetAllType('cfile', function (err, docs) { if (err == null) { if (docs.length == 0) { - console.log('File not found.'); + console.log("File not found."); } else { for (var i in docs) { const file = docs[i]._id.split('/')[1], binary = obj.db.decryptData(obj.args.configkey, docs[i].data); if (binary == null) { - console.log('Invalid config key.'); + console.log("Invalid config key."); } else { var fullFileName = obj.path.join(obj.args.dbpullconfigfiles, file); try { obj.fs.writeFileSync(fullFileName, binary); } catch (ex) { console.log('Unable to write to ' + fullFileName); process.exit(); return; } @@ -476,7 +476,7 @@ function CreateMeshCentralServer(config, args) { } } } else { - console.log('Unable to read from database.'); + console.log("Unable to read from database."); } process.exit(); }); @@ -603,10 +603,10 @@ function CreateMeshCentralServer(config, args) { var key = null; if (typeof obj.args.configkey == 'string') { key = obj.args.configkey; } else if (typeof obj.args.loadconfigfromdb == 'string') { key = obj.args.loadconfigfromdb; } - if (key == null) { console.log('Error, --configkey is required.'); process.exit(); return; } + if (key == null) { console.log("Error, --configkey is required."); process.exit(); return; } obj.db.getAllConfigFiles(key, function (configFiles) { - if (configFiles == null) { console.log('Error, no configuration files found or invalid configkey.'); process.exit(); return; } - if (!configFiles['config.json']) { console.log('Error, could not file config.json from database.'); process.exit(); return; } + if (configFiles == null) { console.log("Error, no configuration files found or invalid configkey."); process.exit(); return; } + if (!configFiles['config.json']) { console.log("Error, could not file config.json from database."); process.exit(); return; } obj.configurationFiles = configFiles; // Parse the new configuration file @@ -744,9 +744,9 @@ function CreateMeshCentralServer(config, args) { var adminname = obj.args.admin.split('/'); if (adminname.length == 1) { adminname = 'user//' + adminname[0]; } else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; } - else { console.log('Invalid administrator name.'); process.exit(); return; } + else { console.log("Invalid administrator name."); process.exit(); return; } obj.db.Get(adminname, function (err, user) { - if (user.length != 1) { console.log('Invalid user name.'); process.exit(); return; } + if (user.length != 1) { console.log("Invalid user name."); process.exit(); return; } user[0].siteadmin = 4294967295; // 0xFFFFFFFF obj.db.Set(user[0], function () { if (user[0].domain == '') { console.log('User ' + user[0].name + ' set to site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' set to site administrator.'); } @@ -762,9 +762,9 @@ function CreateMeshCentralServer(config, args) { var adminname = obj.args.unadmin.split('/'); if (adminname.length == 1) { adminname = 'user//' + adminname[0]; } else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; } - else { console.log('Invalid administrator name.'); process.exit(); return; } + else { console.log("Invalid administrator name."); process.exit(); return; } obj.db.Get(adminname, function (err, user) { - if (user.length != 1) { console.log('Invalid user name.'); process.exit(); return; } + if (user.length != 1) { console.log("Invalid user name."); process.exit(); return; } if (user[0].siteadmin) { delete user[0].siteadmin; } obj.db.Set(user[0], function () { if (user[0].domain == '') { console.log('User ' + user[0].name + ' is not a site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' is not a site administrator.'); } @@ -793,15 +793,15 @@ function CreateMeshCentralServer(config, args) { while (obj.dbconfig.amtWsEventSecret == null) { process.nextTick(); } var username = buf.toString('hex'); var nodeid = obj.args.getwspass; - var pass = obj.crypto.createHash('sha384').update(username.toLowerCase() + ":" + nodeid + ":" + obj.dbconfig.amtWsEventSecret).digest("base64").substring(0, 12).split("/").join("x").split("\\").join("x"); - console.log('--- Intel(r) AMT WSMAN eventing credentials ---'); - console.log('Username: ' + username); - console.log('Password: ' + pass); - console.log('Argument: ' + nodeid); + var pass = obj.crypto.createHash('sha384').update(username.toLowerCase() + ':' + nodeid + ':' + obj.dbconfig.amtWsEventSecret).digest('base64').substring(0, 12).split('/').join('x').split('\\').join('x'); + console.log("--- Intel(r) AMT WSMAN eventing credentials ---"); + console.log("Username: " + username); + console.log("Password: " + pass); + console.log("Argument: " + nodeid); process.exit(); }); } else { - console.log('Invalid NodeID.'); + console.log("Invalid NodeID."); process.exit(); } return; @@ -809,7 +809,7 @@ function CreateMeshCentralServer(config, args) { // Start plugin manager if configuration allows this. if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) { - obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj); + obj.pluginHandler = require('./pluginHandler.js').pluginHandler(obj); } // Load the default meshcore and meshcmd @@ -838,7 +838,7 @@ function CreateMeshCentralServer(config, args) { if (obj.letsencrypt != null) { obj.letsencrypt.getCertificate(certs, obj.StartEx3); // Use Let's Encrypt certificate } else { - console.log('ERROR: Unable to setup GreenLock module.'); + console.log("ERROR: Unable to setup GreenLock module."); obj.StartEx3(certs); // Let's Encrypt did not load, just use the configured certificates } } @@ -847,50 +847,23 @@ function CreateMeshCentralServer(config, args) { // Start the server with the given certificates, but check if we have web certificates to load obj.StartEx3 = function (certs) { - var i, webCertLoadCount = 0; obj.certificates = certs; obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators // Load any domain web certificates - for (i in obj.config.domains) { + for (var i in obj.config.domains) { // Load any Intel AMT ACM activation certificates obj.certificateOperations.loadIntelAmtAcmCerts(obj.config.domains[i].amtacmactivation); - if (obj.config.domains[i].certurl != null) { - // Fix the URL and add 'https://' if needed + if (typeof obj.config.domains[i].certurl == 'string') { + obj.supportsProxyCertificatesRequest = true; // If a certurl is set, enable proxy cert requests + // Then, fix the URL and add 'https://' if needed if (obj.config.domains[i].certurl.indexOf('://') < 0) { obj.config.domains[i].certurl = 'https://' + obj.config.domains[i].certurl; } - - // Load web certs - webCertLoadCount++; - var dnsname = obj.config.domains[i].dns; - if ((dnsname == null) && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; } - obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, dnsname, obj.config.domains[i], function (url, cert, xhostname, xdomain) { - if (cert != null) { - // Hash the entire cert - var hash = obj.crypto.createHash('sha384').update(Buffer.from(cert, 'binary')).digest('hex'); - if (xdomain.certhash != hash) { xdomain.certkeyhash = hash; xdomain.certhash = hash; } - - try { - // Decode a RSA certificate and hash the public key, if this is not RSA, skip this. - var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert)); - xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }); - //console.log('V1: ' + xdomain.certkeyhash); - } catch (ex) { } - - console.log('Loaded web certificate from \"' + url + '\", host: \"' + xhostname + '\"'); - console.log(' SHA384 cert hash: ' + xdomain.certhash); - if (xdomain.certhash != xdomain.certkeyhash) { console.log(' SHA384 key hash: ' + xdomain.certkeyhash); } - } else { - console.log('Failed to load web certificate at: \"' + url + '\", host: \"' + xhostname + '\"'); - } - webCertLoadCount--; - if (webCertLoadCount == 0) { obj.StartEx4(); } // Done loading all certificates - }); } } - // No certificate to load, start the server - if (webCertLoadCount == 0) { obj.StartEx4(); } + if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(); } + obj.StartEx4(); // Keep going } // Start the server with the given certificates @@ -903,7 +876,7 @@ function CreateMeshCentralServer(config, args) { // Write server version and run mode var productionMode = (process.env.NODE_ENV && (process.env.NODE_ENV == 'production')); var runmode = (obj.args.lanonly ? 2 : (obj.args.wanonly ? 1 : 0)); - console.log('MeshCentral v' + obj.currentVer + ', ' + (['Hybrid (LAN + WAN) mode', 'WAN mode', 'LAN mode'][runmode]) + (productionMode ? ', Production mode.' : '.')); + console.log("MeshCentral v" + obj.currentVer + ', ' + (["Hybrid (LAN + WAN) mode", "WAN mode", "LAN mode"][runmode]) + (productionMode ? ", Production mode." : '.')); // Check that no sub-domains have the same DNS as the parent for (i in obj.config.domains) { @@ -1029,9 +1002,9 @@ function CreateMeshCentralServer(config, args) { obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats }, 300000); - obj.debug('main', 'Server started'); + obj.debug('main', "Server started"); if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); } - obj.updateServerState('state', 'running'); + obj.updateServerState('state', "running"); // Setup auto-backup defaults if (obj.config.settings.autobackup == null) { obj.config.settings.autobackup = { backupintervalhours: 24, keeplastdaysbackup: 10 }; } @@ -1045,6 +1018,61 @@ function CreateMeshCentralServer(config, args) { }); }; + // Refresh any certificate hashs from the reverse proxy + obj.pendingProxyCertificatesRequests = 0; + obj.lastProxyCertificatesRequest = null; + obj.supportsProxyCertificatesRequest = false; + obj.updateProxyCertificates = function () { + var i; + if ((obj.pendingProxyCertificatesRequests > 0) || (obj.supportsProxyCertificatesRequest == false)) { return; } + if ((obj.lastProxyCertificatesRequest != null) && ((Date.now() - obj.lastProxyCertificatesRequest) < 120000)) { return; } // Don't allow this call more than every 2 minutes. + obj.lastProxyCertificatesRequest = Date.now(); + + // Load any domain web certificates + for (i in obj.config.domains) { + if (obj.config.domains[i].certurl != null) { + // Load web certs + obj.pendingProxyCertificatesRequests++; + var dnsname = obj.config.domains[i].dns; + if ((dnsname == null) && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; } + obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, dnsname, obj.config.domains[i], function (url, cert, xhostname, xdomain) { + obj.pendingProxyCertificatesRequests--; + if (cert != null) { + // Hash the entire cert + var hash = obj.crypto.createHash('sha384').update(Buffer.from(cert, 'binary')).digest('hex'); + if (xdomain.certhash != hash) { // The certificate has changed. + xdomain.certkeyhash = hash; + xdomain.certhash = hash; + + try { + // Decode a RSA certificate and hash the public key, if this is not RSA, skip this. + var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert)); + xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }); + //console.log('V1: ' + xdomain.certkeyhash); + } catch (ex) { + delete xdomain.certkeyhash; + } + + if (obj.webserver) { + obj.webserver.webCertificateHashs[xdomain.id] = obj.webserver.webCertificateFullHashs[xdomain.id] = Buffer.from(hash, 'hex').toString('binary'); + if (xdomain.certkeyhash != null) { obj.webserver.webCertificateHashs[xdomain.id] = Buffer.from(xdomain.certkeyhash, 'hex').toString('binary'); } + + // Disconnect all agents with bad web certificates + for (var i in obj.webserver.wsagentsWithBadWebCerts) { obj.webserver.wsagentsWithBadWebCerts[i].close(1); } + } + + console.log(obj.common.format("Loaded web certificate from \"{0}\", host: \"{1}\"", url, xhostname)); + console.log(obj.common.format(" SHA384 cert hash: {0}", xdomain.certhash)); + if ((xdomain.certkeyhash != null) && (xdomain.certhash != xdomain.certkeyhash)) { console.log(obj.common.format(" SHA384 key hash: {0}", xdomain.certkeyhash)); } + } + } else { + console.log(obj.common.format("Failed to load web certificate at: \"{0}\", host: \"{1}\"", url, xhostname)); + } + }); + } + } + } + // Perform maintenance operations (called every hour) obj.maintenanceActions = function () { // Check for self-update that targets a specific version @@ -1068,19 +1096,19 @@ function CreateMeshCentralServer(config, args) { if (!obj.db) return; // Dispatch an event saying the server is now stopping - obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' }); + obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: "Server stopped" }); // Set all nodes to power state of unknown (0) obj.db.storePowerEvent({ time: new Date(), nodeid: '*', power: 0, s: 2 }, obj.multiServer, function () { // s:2 indicates that the server is shutting down. if (restoreFile) { - obj.debug('main', 'Server stopped, updating settings: ' + restoreFile); - console.log('Updating settings folder...'); + obj.debug('main', obj.common.format("Server stopped, updating settings: {0}", restoreFile)); + console.log("Updating settings folder..."); - var yauzl = require("yauzl"); + var yauzl = require('yauzl'); yauzl.open(restoreFile, { lazyEntries: true }, function (err, zipfile) { if (err) throw err; zipfile.readEntry(); - zipfile.on("entry", function (entry) { + zipfile.on('entry', function (entry) { if (/\/$/.test(entry.fileName)) { // Directory file names end with '/'. // Note that entires for directories themselves are optional. @@ -1090,22 +1118,22 @@ function CreateMeshCentralServer(config, args) { // file entry zipfile.openReadStream(entry, function (err, readStream) { if (err) throw err; - readStream.on("end", function () { zipfile.readEntry(); }); + readStream.on('end', function () { zipfile.readEntry(); }); // console.log('Extracting:', obj.getConfigFilePath(entry.fileName)); readStream.pipe(obj.fs.createWriteStream(obj.getConfigFilePath(entry.fileName))); }); } }); - zipfile.on("end", function () { setTimeout(function () { obj.fs.unlinkSync(restoreFile); process.exit(123); }); }); + zipfile.on('end', function () { setTimeout(function () { obj.fs.unlinkSync(restoreFile); process.exit(123); }); }); }); } else { - obj.debug('main', 'Server stopped'); + obj.debug('main', "Server stopped"); process.exit(0); } }); // Update the server state - obj.updateServerState('state', 'stopped'); + obj.updateServerState('state', "stopped"); }; // Event Dispatch diff --git a/package.json b/package.json index a2dd4bb8..4484ed1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.3-o", + "version": "0.4.3-p", "keywords": [ "Remote Management", "Intel AMT", diff --git a/webserver.js b/webserver.js index 6fd34bbf..2ea78c05 100644 --- a/webserver.js +++ b/webserver.js @@ -156,20 +156,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } // Main lists - obj.wsagents = {}; // NodeId --> Agent + obj.wsagents = {}; // NodeId --> Agent + obj.wsagentsWithBadWebCerts = {}; // NodeId --> Agent obj.wsagentsDisconnections = {}; obj.wsagentsDisconnectionsTimer = null; obj.duplicateAgentsLog = {}; - obj.wssessions = {}; // UserId --> Array Of Sessions - obj.wssessions2 = {}; // "UserId + SessionRnd" --> Session (Note that the SessionId is the UserId + / + SessionRnd) - obj.wsPeerSessions = {}; // ServerId --> Array Of "UserId + SessionRnd" - obj.wsPeerSessions2 = {}; // "UserId + SessionRnd" --> ServerId - obj.wsPeerSessions3 = {}; // ServerId --> UserId --> [ SessionId ] - obj.sessionsCount = {}; // Merged session counters, used when doing server peering. UserId --> SessionCount - obj.wsrelays = {}; // Id -> Relay - obj.wsPeerRelays = {}; // Id -> { ServerId, Time } - var tlsSessionStore = {}; // Store TLS session information for quick resume. - var tlsSessionStoreCount = 0; // Number of cached TLS session information in store. + obj.wssessions = {}; // UserId --> Array Of Sessions + obj.wssessions2 = {}; // "UserId + SessionRnd" --> Session (Note that the SessionId is the UserId + / + SessionRnd) + obj.wsPeerSessions = {}; // ServerId --> Array Of "UserId + SessionRnd" + obj.wsPeerSessions2 = {}; // "UserId + SessionRnd" --> ServerId + obj.wsPeerSessions3 = {}; // ServerId --> UserId --> [ SessionId ] + obj.sessionsCount = {}; // Merged session counters, used when doing server peering. UserId --> SessionCount + obj.wsrelays = {}; // Id -> Relay + obj.wsPeerRelays = {}; // Id -> { ServerId, Time } + var tlsSessionStore = {}; // Store TLS session information for quick resume. + var tlsSessionStoreCount = 0; // Number of cached TLS session information in store. // Setup randoms obj.crypto.randomBytes(48, function (err, buf) { obj.httpAuthRandom = buf; }); From 81e79019d6124f6004d66052b2b501a1adc78792 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 29 Oct 2019 16:31:03 -0700 Subject: [PATCH 6/9] Reverse proxy improvements. --- meshagent.js | 2 +- meshcentral.js | 15 ++++++++------- package.json | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/meshagent.js b/meshagent.js index 62b781c1..51282ad6 100644 --- a/meshagent.js +++ b/meshagent.js @@ -387,7 +387,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if (parent.parent.supportsProxyCertificatesRequest !== false) { obj.badWebCert = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); parent.wsagentsWithBadWebCerts[obj.badWebCert] = obj; // Add this agent to the list of of agents with bad web certificates. - parent.parent.updateProxyCertificates(); + parent.parent.updateProxyCertificates(false); } parent.agentStats.agentBadWebCertHashCount++; console.log('Agent bad web cert hash (Agent:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (Buffer.from(getWebCertHash(domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').'); diff --git a/meshcentral.js b/meshcentral.js index 4ed6c506..a1ceee15 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -862,7 +862,7 @@ function CreateMeshCentralServer(config, args) { } } - if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(); } + if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(true); } obj.StartEx4(); // Keep going } @@ -1022,14 +1022,15 @@ function CreateMeshCentralServer(config, args) { obj.pendingProxyCertificatesRequests = 0; obj.lastProxyCertificatesRequest = null; obj.supportsProxyCertificatesRequest = false; - obj.updateProxyCertificates = function () { - var i; - if ((obj.pendingProxyCertificatesRequests > 0) || (obj.supportsProxyCertificatesRequest == false)) { return; } - if ((obj.lastProxyCertificatesRequest != null) && ((Date.now() - obj.lastProxyCertificatesRequest) < 120000)) { return; } // Don't allow this call more than every 2 minutes. - obj.lastProxyCertificatesRequest = Date.now(); + obj.updateProxyCertificates = function (force) { + if (force !== true) { + if ((obj.pendingProxyCertificatesRequests > 0) || (obj.supportsProxyCertificatesRequest == false)) return; + if ((obj.lastProxyCertificatesRequest != null) && ((Date.now() - obj.lastProxyCertificatesRequest) < 120000)) return; // Don't allow this call more than every 2 minutes. + obj.lastProxyCertificatesRequest = Date.now(); + } // Load any domain web certificates - for (i in obj.config.domains) { + for (var i in obj.config.domains) { if (obj.config.domains[i].certurl != null) { // Load web certs obj.pendingProxyCertificatesRequests++; diff --git a/package.json b/package.json index 4484ed1f..f65aa6eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.3-p", + "version": "0.4.3-q", "keywords": [ "Remote Management", "Intel AMT", From cba8476f61f61c55ac4836fa89d2c4112e6f3fc6 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 30 Oct 2019 10:25:36 -0700 Subject: [PATCH 7/9] Added file upload overwrite confirm prompt for server files. --- views/default-min.handlebars | 38 +++++++++++++++++--- views/default.handlebars | 38 +++++++++++++++++--- views/translations/default-min_fr.handlebars | 38 +++++++++++++++++--- views/translations/default_fr.handlebars | 38 +++++++++++++++++--- 4 files changed, 132 insertions(+), 20 deletions(-) diff --git a/views/default-min.handlebars b/views/default-min.handlebars index ef8d2e9c..2667d095 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -5163,6 +5163,7 @@ function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); } function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } } function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, ''); updateUploadDialogOk('p13uploadinput'); } + function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q('p13uploadinput').files.length > 0); } function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); } function p13viewfile() { var checkboxes = document.getElementsByName('fd'); @@ -6840,9 +6841,35 @@ function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); } function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } } var isFilenameValid = (function(){ var x1=/^[^\\/:\*\?"<>\|]+$/, x2=/^\./, x3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname){ return x1.test(fname)&&!x2.test(fname)&&!x3.test(fname)&&(fname[0] != '.'); } })(); - function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); updateUploadDialogOk('p5uploadinput'); } + function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); p5updateUploadDialogOk('p5uploadinput'); } function p5uploadFileEx() { Q('p5loginSubmit').click(); } - function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); } + function p5updateUploadDialogOk() { + // Check if these are files we can upload, remove all folders. + var xallfiles = Q('p5uploadinput').files, files = []; + for (var i in xallfiles) { if ((xallfiles[i].size != null) && (xallfiles[i].size != 0)) { files.push(xallfiles[i]); } } + + // Check if these files are duplicates of existing files. + var filetreex = filetree, allfiles = [], overWriteCount = 0; + for (var i in filetreelocation) { + if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; } + } + QE('idx_dlgOkButton', xallfiles.length > 0); + if (xallfiles.length > 0) { + if (filetreex.f != null) { + for (var i in filetreex.f) { allfiles.push(i); } + for (var i = 0; i < xallfiles.length; i++) { + if (allfiles.indexOf(xallfiles[i].name) >= 0) { overWriteCount++; } // TODO: If the server is Windows, we need to lowercase both names. + } + } + QV('p5confirmOverwriteSpan', overWriteCount > 0); + if (overWriteCount > 0) { + QE('idx_dlgOkButton', Q('p5confirmOverwrite').checked); + } else { + Q('p5confirmOverwrite').checked = false; + QE('idx_dlgOkButton', true); + } + } + } /* function p5viewfile() { var checkboxes = document.getElementsByName('fc'); @@ -7312,8 +7339,9 @@ } function showCreateNewAccountDialogValidate(x) { - var ve = validateEmail(Q('p4email').value); + var ve = true; if (serverinfo.emailcheck) { + ve = validateEmail(Q('p4email').value); QE('p4verifiedEmail', ve); QE('p4invitationEmail', ve && Q('p4resetNextLogin').checked && Q('p4verifiedEmail').checked); if (ve == false) { Q('p4verifiedEmail').checked = false; } @@ -7580,8 +7608,8 @@ function p30showUserChangePassDialog(multiFactor) { if (xxdialogMode) return; var x = ''; - x += addHtmlValue("Password", ''); - x += addHtmlValue("Password", ''); + x += addHtmlValue("Password", ''); + x += addHtmlValue("Password", ''); if (features & 0x00010000) { x += addHtmlValue("Password hint", ''); } if (passRequirements) { diff --git a/views/default.handlebars b/views/default.handlebars index 5793477b..a044fa68 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -6145,6 +6145,7 @@ function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); } function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } } function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, ''); updateUploadDialogOk('p13uploadinput'); } + function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q('p13uploadinput').files.length > 0); } function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); } function p13viewfile() { var checkboxes = document.getElementsByName('fd'); @@ -7822,9 +7823,35 @@ function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); } function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } } var isFilenameValid = (function(){ var x1=/^[^\\/:\*\?"<>\|]+$/, x2=/^\./, x3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname){ return x1.test(fname)&&!x2.test(fname)&&!x3.test(fname)&&(fname[0] != '.'); } })(); - function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); updateUploadDialogOk('p5uploadinput'); } + function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); p5updateUploadDialogOk('p5uploadinput'); } function p5uploadFileEx() { Q('p5loginSubmit').click(); } - function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); } + function p5updateUploadDialogOk() { + // Check if these are files we can upload, remove all folders. + var xallfiles = Q('p5uploadinput').files, files = []; + for (var i in xallfiles) { if ((xallfiles[i].size != null) && (xallfiles[i].size != 0)) { files.push(xallfiles[i]); } } + + // Check if these files are duplicates of existing files. + var filetreex = filetree, allfiles = [], overWriteCount = 0; + for (var i in filetreelocation) { + if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; } + } + QE('idx_dlgOkButton', xallfiles.length > 0); + if (xallfiles.length > 0) { + if (filetreex.f != null) { + for (var i in filetreex.f) { allfiles.push(i); } + for (var i = 0; i < xallfiles.length; i++) { + if (allfiles.indexOf(xallfiles[i].name) >= 0) { overWriteCount++; } // TODO: If the server is Windows, we need to lowercase both names. + } + } + QV('p5confirmOverwriteSpan', overWriteCount > 0); + if (overWriteCount > 0) { + QE('idx_dlgOkButton', Q('p5confirmOverwrite').checked); + } else { + Q('p5confirmOverwrite').checked = false; + QE('idx_dlgOkButton', true); + } + } + } /* function p5viewfile() { var checkboxes = document.getElementsByName('fc'); @@ -8294,8 +8321,9 @@ } function showCreateNewAccountDialogValidate(x) { - var ve = validateEmail(Q('p4email').value); + var ve = true; if (serverinfo.emailcheck) { + ve = validateEmail(Q('p4email').value); QE('p4verifiedEmail', ve); QE('p4invitationEmail', ve && Q('p4resetNextLogin').checked && Q('p4verifiedEmail').checked); if (ve == false) { Q('p4verifiedEmail').checked = false; } @@ -8562,8 +8590,8 @@ function p30showUserChangePassDialog(multiFactor) { if (xxdialogMode) return; var x = ''; - x += addHtmlValue("Password", ''); - x += addHtmlValue("Password", ''); + x += addHtmlValue("Password", ''); + x += addHtmlValue("Password", ''); if (features & 0x00010000) { x += addHtmlValue("Password hint", ''); } if (passRequirements) { diff --git a/views/translations/default-min_fr.handlebars b/views/translations/default-min_fr.handlebars index d6532975..103b492d 100644 --- a/views/translations/default-min_fr.handlebars +++ b/views/translations/default-min_fr.handlebars @@ -5163,6 +5163,7 @@ function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); } function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } } function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, ''); updateUploadDialogOk('p13uploadinput'); } + function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q('p13uploadinput').files.length > 0); } function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); } function p13viewfile() { var checkboxes = document.getElementsByName('fd'); @@ -6840,9 +6841,35 @@ function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); } function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } } var isFilenameValid = (function(){ var x1=/^[^\\/:\*\?"<>\|]+$/, x2=/^\./, x3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname){ return x1.test(fname)&&!x2.test(fname)&&!x3.test(fname)&&(fname[0] != '.'); } })(); - function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); updateUploadDialogOk('p5uploadinput'); } + function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); p5updateUploadDialogOk('p5uploadinput'); } function p5uploadFileEx() { Q('p5loginSubmit').click(); } - function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); } + function p5updateUploadDialogOk() { + // Check if these are files we can upload, remove all folders. + var xallfiles = Q('p5uploadinput').files, files = []; + for (var i in xallfiles) { if ((xallfiles[i].size != null) && (xallfiles[i].size != 0)) { files.push(xallfiles[i]); } } + + // Check if these files are duplicates of existing files. + var filetreex = filetree, allfiles = [], overWriteCount = 0; + for (var i in filetreelocation) { + if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; } + } + QE('idx_dlgOkButton', xallfiles.length > 0); + if (xallfiles.length > 0) { + if (filetreex.f != null) { + for (var i in filetreex.f) { allfiles.push(i); } + for (var i = 0; i < xallfiles.length; i++) { + if (allfiles.indexOf(xallfiles[i].name) >= 0) { overWriteCount++; } // TODO: If the server is Windows, we need to lowercase both names. + } + } + QV('p5confirmOverwriteSpan', overWriteCount > 0); + if (overWriteCount > 0) { + QE('idx_dlgOkButton', Q('p5confirmOverwrite').checked); + } else { + Q('p5confirmOverwrite').checked = false; + QE('idx_dlgOkButton', true); + } + } + } /* function p5viewfile() { var checkboxes = document.getElementsByName('fc'); @@ -7312,8 +7339,9 @@ } function showCreateNewAccountDialogValidate(x) { - var ve = validateEmail(Q('p4email').value); + var ve = true; if (serverinfo.emailcheck) { + ve = validateEmail(Q('p4email').value); QE('p4verifiedEmail', ve); QE('p4invitationEmail', ve && Q('p4resetNextLogin').checked && Q('p4verifiedEmail').checked); if (ve == false) { Q('p4verifiedEmail').checked = false; } @@ -7580,8 +7608,8 @@ function p30showUserChangePassDialog(multiFactor) { if (xxdialogMode) return; var x = ''; - x += addHtmlValue("Mot de passe", ''); - x += addHtmlValue("Mot de passe", ''); + x += addHtmlValue("Mot de passe", ''); + x += addHtmlValue("Mot de passe", ''); if (features & 0x00010000) { x += addHtmlValue("Password hint", ''); } if (passRequirements) { diff --git a/views/translations/default_fr.handlebars b/views/translations/default_fr.handlebars index 979d3ab0..4d0b2a12 100644 --- a/views/translations/default_fr.handlebars +++ b/views/translations/default_fr.handlebars @@ -6143,6 +6143,7 @@ function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); } function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } } function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, ''); updateUploadDialogOk('p13uploadinput'); } + function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q('p13uploadinput').files.length > 0); } function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); } function p13viewfile() { var checkboxes = document.getElementsByName('fd'); @@ -7820,9 +7821,35 @@ function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); } function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } } var isFilenameValid = (function(){ var x1=/^[^\\/:\*\?"<>\|]+$/, x2=/^\./, x3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname){ return x1.test(fname)&&!x2.test(fname)&&!x3.test(fname)&&(fname[0] != '.'); } })(); - function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); updateUploadDialogOk('p5uploadinput'); } + function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); p5updateUploadDialogOk('p5uploadinput'); } function p5uploadFileEx() { Q('p5loginSubmit').click(); } - function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); } + function p5updateUploadDialogOk() { + // Check if these are files we can upload, remove all folders. + var xallfiles = Q('p5uploadinput').files, files = []; + for (var i in xallfiles) { if ((xallfiles[i].size != null) && (xallfiles[i].size != 0)) { files.push(xallfiles[i]); } } + + // Check if these files are duplicates of existing files. + var filetreex = filetree, allfiles = [], overWriteCount = 0; + for (var i in filetreelocation) { + if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; } + } + QE('idx_dlgOkButton', xallfiles.length > 0); + if (xallfiles.length > 0) { + if (filetreex.f != null) { + for (var i in filetreex.f) { allfiles.push(i); } + for (var i = 0; i < xallfiles.length; i++) { + if (allfiles.indexOf(xallfiles[i].name) >= 0) { overWriteCount++; } // TODO: If the server is Windows, we need to lowercase both names. + } + } + QV('p5confirmOverwriteSpan', overWriteCount > 0); + if (overWriteCount > 0) { + QE('idx_dlgOkButton', Q('p5confirmOverwrite').checked); + } else { + Q('p5confirmOverwrite').checked = false; + QE('idx_dlgOkButton', true); + } + } + } /* function p5viewfile() { var checkboxes = document.getElementsByName('fc'); @@ -8292,8 +8319,9 @@ } function showCreateNewAccountDialogValidate(x) { - var ve = validateEmail(Q('p4email').value); + var ve = true; if (serverinfo.emailcheck) { + ve = validateEmail(Q('p4email').value); QE('p4verifiedEmail', ve); QE('p4invitationEmail', ve && Q('p4resetNextLogin').checked && Q('p4verifiedEmail').checked); if (ve == false) { Q('p4verifiedEmail').checked = false; } @@ -8560,8 +8588,8 @@ function p30showUserChangePassDialog(multiFactor) { if (xxdialogMode) return; var x = ''; - x += addHtmlValue("Mot de passe", ''); - x += addHtmlValue("Mot de passe", ''); + x += addHtmlValue("Mot de passe", ''); + x += addHtmlValue("Mot de passe", ''); if (features & 0x00010000) { x += addHtmlValue("Password hint", ''); } if (passRequirements) { From 3c4f6f6bb7b1f78cce1307f52d0a88797924fcae Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 30 Oct 2019 14:30:34 -0700 Subject: [PATCH 8/9] New MeshMessenger and fixed server crash on some file copy. --- meshuser.js | 36 ++++++++++++------- package.json | 2 +- public/styles/messenger.css | 2 +- views/default-min.handlebars | 21 +++++++---- views/default.handlebars | 21 +++++++---- views/messenger-min.handlebars | 2 +- views/messenger.handlebars | 17 ++++----- views/translations/default-min_fr.handlebars | 21 +++++++---- views/translations/default_fr.handlebars | 21 +++++++---- .../translations/messenger-min_fr.handlebars | 2 +- views/translations/messenger_fr.handlebars | 17 ++++----- webserver.js | 12 ++++--- 12 files changed, 112 insertions(+), 62 deletions(-) diff --git a/meshuser.js b/meshuser.js index 8d817960..4c43f42c 100644 --- a/meshuser.js +++ b/meshuser.js @@ -120,19 +120,29 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } else return null; var rootfolder = meshpath[0], rootfoldersplit = rootfolder.split('/'), domainx = 'domain'; if (rootfoldersplit[1].length > 0) domainx = 'domain-' + rootfoldersplit[1]; - var path = parent.path.join(parent.filespath, domainx, rootfoldersplit[0] + "-" + rootfoldersplit[2]); + var path = parent.path.join(parent.filespath, domainx, rootfoldersplit[0] + '-' + rootfoldersplit[2]); for (var i = 1; i < meshpath.length; i++) { if (common.IsFilenameValid(meshpath[i]) == false) { path = null; break; } path += ("/" + meshpath[i]); } return path; } - // TODO: Replace this with something better? + // Copy a file using the best technique available function copyFile(src, dest, func, tag) { - var ss = fs.createReadStream(src), ds = fs.createWriteStream(dest); - ss.pipe(ds); - ds.ss = ss; - if (arguments.length == 3 && typeof arguments[2] === 'function') { ds.on('close', arguments[2]); } - else if (arguments.length == 4 && typeof arguments[3] === 'function') { ds.on('close', arguments[3]); } - ds.on('close', function () { func(tag); }); + if (fs.copyFile) { + // NodeJS v8.5 and higher + fs.copyFile(src, dest, function (err) { func(tag); }) + } else { + // Older NodeJS + try { + var ss = fs.createReadStream(src), ds = fs.createWriteStream(dest); + ss.on('error', function () { func(tag); }); + ds.on('error', function () { func(tag); }); + ss.pipe(ds); + ds.ss = ss; + if (arguments.length == 3 && typeof arguments[2] === 'function') { ds.on('close', arguments[2]); } + else if (arguments.length == 4 && typeof arguments[3] === 'function') { ds.on('close', arguments[3]); } + ds.on('close', function () { func(tag); }); + } catch (ex) { } + } } // Route a command to a target node @@ -607,9 +617,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((command.fileop == 'createfolder') && (common.IsFilenameValid(command.newfolder) == true)) { // Create a new folder - try { fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { - try { fs.mkdirSync(path); } catch (e) { } - try { fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { } + try { fs.mkdirSync(path + '/' + command.newfolder); } catch (ex) { + try { fs.mkdirSync(path); } catch (ex) { } + try { fs.mkdirSync(path + '/' + command.newfolder); } catch (ex) { } } } else if (command.fileop == 'delete') { @@ -619,9 +629,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = parent.path.join(path, command.delfiles[i]); if (command.rec == true) { - deleteFolderRecursive(fullpath); // TODO, make this an async function + try { deleteFolderRecursive(fullpath); } catch (ex) { } // TODO, make this an async function } else { - try { fs.rmdirSync(fullpath); } catch (e) { try { fs.unlinkSync(fullpath); } catch (e) { } } + try { fs.rmdirSync(fullpath); } catch (ex) { try { fs.unlinkSync(fullpath); } catch (xe) { } } } } } diff --git a/package.json b/package.json index f65aa6eb..19765991 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.3-q", + "version": "0.4.3-r", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/styles/messenger.css b/public/styles/messenger.css index acc3cecc..74f419d2 100644 --- a/public/styles/messenger.css +++ b/public/styles/messenger.css @@ -3,10 +3,10 @@ border: none; margin: 2px; margin-top: 3px; - float: right; border-radius: 3px; height: 32px; width: 32px; + float: right; } .topButton:hover { diff --git a/views/default-min.handlebars b/views/default-min.handlebars index 2667d095..ac69b0ef 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -3524,7 +3524,7 @@ if (xxdialogMode) return; var url = '/messenger?id=meshmessenger/' + encodeURIComponent(currentNode._id) + '/' + encodeURIComponent(userinfo._id) + '&title=' + currentNode.name; if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; } - window.open(url, 'meshmessenger:' + currentNode._id); + window.open(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560'); meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) }); } @@ -6740,12 +6740,12 @@ var h = ''; if (f.t < 3 || f.t == 4) { var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = ''; - h = '
 ' + right + '
' + shortname + '
'; + h = '
 ' + right + '
' + shortname + '
'; } else { var link = shortname, publiclink = ''; - if (publicfolder) { publiclink = ' (' + "Link" + ')'; } - if (f.s > 0) { link = '' + shortname + '' + publiclink; } - h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; + if (publicfolder) { publiclink = ' (' + "Link" + ')'; } + if (f.s > 0) { link = '' + shortname + '' + publiclink; } + h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; } if (f.t < 3) { html1 += h; } else { html2 += h; } @@ -6883,7 +6883,16 @@ */ var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; - function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); } + function p5copyFile(cut) { + var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); + for (var i = 0; i < checkboxes.length; i++) { + if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { + console.log('yy', checkboxes[i].value); + p5clipboard.push(checkboxes[i].value); + } + } + p5updateClipview(); + } function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confim {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0?'copy':'move'), p5clipboard.length, ((p5clipboard.length > 1)?'s':'')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); } function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0?'copy':'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } } function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1)?'s':''), (p5clipboardCut == 0?"copy":"move")) + ', ' + "Clear" + '.' } QH('p5bottomstatus', x); p5setActions(); } diff --git a/views/default.handlebars b/views/default.handlebars index a044fa68..b4df2d15 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -4506,7 +4506,7 @@ if (xxdialogMode) return; var url = '/messenger?id=meshmessenger/' + encodeURIComponent(currentNode._id) + '/' + encodeURIComponent(userinfo._id) + '&title=' + currentNode.name; if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; } - window.open(url, 'meshmessenger:' + currentNode._id); + window.open(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560'); meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) }); } @@ -7722,12 +7722,12 @@ var h = ''; if (f.t < 3 || f.t == 4) { var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = ''; - h = '
 ' + right + '
' + shortname + '
'; + h = '
 ' + right + '
' + shortname + '
'; } else { var link = shortname, publiclink = ''; - if (publicfolder) { publiclink = ' (' + "Link" + ')'; } - if (f.s > 0) { link = '' + shortname + '' + publiclink; } - h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; + if (publicfolder) { publiclink = ' (' + "Link" + ')'; } + if (f.s > 0) { link = '' + shortname + '' + publiclink; } + h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; } if (f.t < 3) { html1 += h; } else { html2 += h; } @@ -7865,7 +7865,16 @@ */ var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; - function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); } + function p5copyFile(cut) { + var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); + for (var i = 0; i < checkboxes.length; i++) { + if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { + console.log('yy', checkboxes[i].value); + p5clipboard.push(checkboxes[i].value); + } + } + p5updateClipview(); + } function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confim {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0?'copy':'move'), p5clipboard.length, ((p5clipboard.length > 1)?'s':'')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); } function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0?'copy':'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } } function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1)?'s':''), (p5clipboardCut == 0?"copy":"move")) + ', ' + "Clear" + '.' } QH('p5bottomstatus', x); p5setActions(); } diff --git a/views/messenger-min.handlebars b/views/messenger-min.handlebars index 5efd3707..26a2e4a6 100644 --- a/views/messenger-min.handlebars +++ b/views/messenger-min.handlebars @@ -1 +1 @@ -MeshMessenger
MeshMessenger
\ No newline at end of file +MeshMessenger
MeshMessenger
\ No newline at end of file diff --git a/views/messenger.handlebars b/views/messenger.handlebars index 8c6931a2..6a8629b2 100644 --- a/views/messenger.handlebars +++ b/views/messenger.handlebars @@ -12,13 +12,14 @@
- - - - - -
-
MeshMessenger
+
+ + + + + +
+
MeshMessenger
@@ -65,7 +66,7 @@ var currentFileDownload = null; // Set the title - if (args.title) { QH('xtitle', ' - ' + args.title); document.title = document.title + ' - ' + args.title; } + if (args.title) { QH('xtitle', args.title.split(' ').join(' ')); document.title = document.title + ' - ' + args.title; } // Setup web notifications if (Notification) { QV('notifyButton', Notification.permission != 'granted'); } diff --git a/views/translations/default-min_fr.handlebars b/views/translations/default-min_fr.handlebars index 103b492d..7b352709 100644 --- a/views/translations/default-min_fr.handlebars +++ b/views/translations/default-min_fr.handlebars @@ -3524,7 +3524,7 @@ if (xxdialogMode) return; var url = '/messenger?id=meshmessenger/' + encodeURIComponent(currentNode._id) + '/' + encodeURIComponent(userinfo._id) + '&title=' + currentNode.name; if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; } - window.open(url, 'meshmessenger:' + currentNode._id); + window.open(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560'); meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) }); } @@ -6740,12 +6740,12 @@ var h = ''; if (f.t < 3 || f.t == 4) { var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = ''; - h = '
 ' + right + '
' + shortname + '
'; + h = '
 ' + right + '
' + shortname + '
'; } else { var link = shortname, publiclink = ''; - if (publicfolder) { publiclink = ' (' + "Link" + ')'; } - if (f.s > 0) { link = '' + shortname + '' + publiclink; } - h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; + if (publicfolder) { publiclink = ' (' + "Link" + ')'; } + if (f.s > 0) { link = '' + shortname + '' + publiclink; } + h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; } if (f.t < 3) { html1 += h; } else { html2 += h; } @@ -6883,7 +6883,16 @@ */ var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; - function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); } + function p5copyFile(cut) { + var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); + for (var i = 0; i < checkboxes.length; i++) { + if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { + console.log('yy', checkboxes[i].value); + p5clipboard.push(checkboxes[i].value); + } + } + p5updateClipview(); + } function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confim {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0?'copy':'move'), p5clipboard.length, ((p5clipboard.length > 1)?'s':'')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); } function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0?'copy':'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } } function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1)?'s':''), (p5clipboardCut == 0?"copy":"move")) + ', ' + "Clear" + '.' } QH('p5bottomstatus', x); p5setActions(); } diff --git a/views/translations/default_fr.handlebars b/views/translations/default_fr.handlebars index 4d0b2a12..64de35c3 100644 --- a/views/translations/default_fr.handlebars +++ b/views/translations/default_fr.handlebars @@ -4504,7 +4504,7 @@ if (xxdialogMode) return; var url = '/messenger?id=meshmessenger/' + encodeURIComponent(currentNode._id) + '/' + encodeURIComponent(userinfo._id) + '&title=' + currentNode.name; if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; } - window.open(url, 'meshmessenger:' + currentNode._id); + window.open(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560'); meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) }); } @@ -7720,12 +7720,12 @@ var h = ''; if (f.t < 3 || f.t == 4) { var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = ''; - h = '
 ' + right + '
' + shortname + '
'; + h = '
 ' + right + '
' + shortname + '
'; } else { var link = shortname, publiclink = ''; - if (publicfolder) { publiclink = ' (' + "Link" + ')'; } - if (f.s > 0) { link = '' + shortname + '' + publiclink; } - h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; + if (publicfolder) { publiclink = ' (' + "Link" + ')'; } + if (f.s > 0) { link = '' + shortname + '' + publiclink; } + h = '
 ' + fdatestr + '' + fsize + '
' + link + '
'; } if (f.t < 3) { html1 += h; } else { html2 += h; } @@ -7863,7 +7863,16 @@ */ var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; - function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); } + function p5copyFile(cut) { + var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); + for (var i = 0; i < checkboxes.length; i++) { + if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { + console.log('yy', checkboxes[i].value); + p5clipboard.push(checkboxes[i].value); + } + } + p5updateClipview(); + } function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confim {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0?'copy':'move'), p5clipboard.length, ((p5clipboard.length > 1)?'s':'')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); } function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0?'copy':'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } } function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1)?'s':''), (p5clipboardCut == 0?"copy":"move")) + ', ' + "Clear" + '.' } QH('p5bottomstatus', x); p5setActions(); } diff --git a/views/translations/messenger-min_fr.handlebars b/views/translations/messenger-min_fr.handlebars index 0832659f..3d6aa1e7 100644 --- a/views/translations/messenger-min_fr.handlebars +++ b/views/translations/messenger-min_fr.handlebars @@ -1 +1 @@ -MeshMessenger
MeshMessenger
\ No newline at end of file +MeshMessenger
MeshMessenger
\ No newline at end of file diff --git a/views/translations/messenger_fr.handlebars b/views/translations/messenger_fr.handlebars index 270eb714..6fb48109 100644 --- a/views/translations/messenger_fr.handlebars +++ b/views/translations/messenger_fr.handlebars @@ -10,13 +10,14 @@
- - - - - -
-
MeshMessenger
+
+ + + + + +
+
MeshMessenger
@@ -63,7 +64,7 @@ var currentFileDownload = null; // Set the title - if (args.title) { QH('xtitle', ' - ' + args.title); document.title = document.title + ' - ' + args.title; } + if (args.title) { QH('xtitle', args.title.split(' ').join(' ')); document.title = document.title + ' - ' + args.title; } // Setup web notifications if (Notification) { QV('notifyButton', Notification.permission != 'granted'); } diff --git a/webserver.js b/webserver.js index 2ea78c05..5409b2ad 100644 --- a/webserver.js +++ b/webserver.js @@ -2691,11 +2691,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Delete a folder and all sub items. (TODO: try to make all async version) function deleteFolderRec(path) { if (obj.fs.existsSync(path) == false) return; - obj.fs.readdirSync(path).forEach(function (file, index) { - var pathx = path + "/" + file; - if (obj.fs.lstatSync(pathx).isDirectory()) { deleteFolderRec(pathx); } else { obj.fs.unlinkSync(pathx); } - }); - obj.fs.rmdirSync(path); + try { + obj.fs.readdirSync(path).forEach(function (file, index) { + var pathx = path + '/' + file; + if (obj.fs.lstatSync(pathx).isDirectory()) { deleteFolderRec(pathx); } else { obj.fs.unlinkSync(pathx); } + }); + obj.fs.rmdirSync(path); + } catch (ex) { } } // Handle Intel AMT events From b679f23f7455697d595d1f86368332c695d1aaa1 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 30 Oct 2019 16:16:17 -0700 Subject: [PATCH 9/9] Shift-chat will now open new tab in browser. --- package.json | 2 +- views/default-min.handlebars | 10 +++++++--- views/default.handlebars | 10 +++++++--- views/translations/default-min_fr.handlebars | 10 +++++++--- views/translations/default_fr.handlebars | 10 +++++++--- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 19765991..1018d726 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.3-r", + "version": "0.4.3-s", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default-min.handlebars b/views/default-min.handlebars index ac69b0ef..6ede98fe 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1,4 +1,4 @@ -{{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

{{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

{{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

{{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}