Merge branch 'master' into plugin-admin

This commit is contained in:
Ryan Blenis 2019-11-01 16:50:42 -04:00
commit b33ca1daff
17 changed files with 420 additions and 186 deletions

View File

@ -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;
}

View File

@ -195,28 +195,29 @@ 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) {
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); }
} else { func(url, null, hostname, tag); }
};
// Check if a configuration file exists
@ -495,13 +496,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++) {

View File

@ -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;

View File

@ -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(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 + ').');
console.log('Agent reported web cert hash:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex')) + '.');

View File

@ -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,48 +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++;
obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, obj.config.domains[i], function (url, cert, 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);
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);
}
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(true); }
obj.StartEx4(); // Keep going
}
// Start the server with the given certificates
@ -901,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) {
@ -1027,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 }; }
@ -1043,6 +1018,62 @@ function CreateMeshCentralServer(config, args) {
});
};
// Refresh any certificate hashs from the reverse proxy
obj.pendingProxyCertificatesRequests = 0;
obj.lastProxyCertificatesRequest = null;
obj.supportsProxyCertificatesRequest = false;
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 (var 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
@ -1066,19 +1097,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.
@ -1088,22 +1119,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

View File

@ -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) { } }
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.4.3-l",
"version": "0.4.3-s",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -3,10 +3,10 @@
border: none;
margin: 2px;
margin-top: 3px;
float: right;
border-radius: 3px;
height: 32px;
width: 32px;
float: right;
}
.topButton:hover {

File diff suppressed because one or more lines are too long

View File

@ -514,7 +514,7 @@
<span id="DeskTimer" title="Session time"></span>&nbsp;
<select id=termdisplays style="display:none" onchange=deskSetDisplay(event) onkeypress="return false" onkeydown="return false"></select>&nbsp;
<input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()" />&nbsp;
<span id=DeskChatButton class="deskarea" title="Open chat window to this computer"><img src='images/icon-chat.png' onclick=deviceChat() height=16 width=16 style=padding-top:2px /></span>
<span id=DeskChatButton class="deskarea" title="Open chat window to this computer"><img src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:2px /></span>
<span id=DeskNotifyButton title="Display a notification on the remote computer"><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:2px /></span>
<span id=DeskOpenWebButton title="Open a web address on remote computer"><img src='images/icon-url2.png' onclick=deviceUrlFunction() height=16 width=16 style=padding-top:2px /></span>
</div>
@ -4549,11 +4549,15 @@
function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); }
function deviceChat() {
function deviceChat(e) {
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);
if (e && (e.shiftKey == true)) {
window.open(url, 'meshmessenger:' + currentNode._id);
} else {
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) });
}
@ -6192,6 +6196,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, '<input type=file name=files id=p13uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p13uploadinput\')" />'); 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');
@ -7768,12 +7773,12 @@
var h = '';
if (f.t < 3 || f.t == 4) {
var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = '';
h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + name + '\'>&nbsp;<span style=float:right title=\"' + title + '\">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p5folderset(\"' + encodeURIComponent(f.nx) + '\")></div><a href=# style=cursor:pointer onclick=\'return p5folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + name + '">&nbsp;<span style=float:right title=\"' + title + '\">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p5folderset(\"' + encodeURIComponent(f.nx) + '\")></div><a href=# style=cursor:pointer onclick=\'return p5folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
} else {
var link = shortname, publiclink = '';
if (publicfolder) { publiclink = ' (<a style=cursor:pointer title=\"Display public link\" onclick=\'return p5showPublicLink(\"' + publicPath + '/' + f.nx + '\")\'>' + "Link" + '</a>)'; }
if (f.s > 0) { link = '<a rel=\"noreferrer noopener\" target=\"_blank\" download href=\"downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '\">' + shortname + '</a>' + publiclink; }
h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + f.nx + '\'>&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
if (publicfolder) { publiclink = ' (<a style=cursor:pointer title="Display public link" onclick=\'return p5showPublicLink("' + publicPath + '/' + f.nx + '")\'>' + "Link" + '</a>)'; }
if (f.s > 0) { link = '<a rel="noreferrer noopener" target="_blank" download href="downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '">' + shortname + '</a>' + publiclink; }
h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + f.nx + '">&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
}
if (f.t < 3) { html1 += h; } else { html2 += h; }
@ -7869,9 +7874,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, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value=\"' + encodeURIComponent(filetreelinkpath) + '\" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /></form>'); updateUploadDialogOk('p5uploadinput'); }
function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value=\"' + encodeURIComponent(filetreelinkpath) + '\" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="p5updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /><span id=p5confirmOverwriteSpan style=display:none><br /><label><input type=checkbox id=p5confirmOverwrite onchange="p5updateUploadDialogOk(\'p5uploadinput\')" />' + "Confirm overwrite?" + '</label></span></form>'); 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');
@ -7885,7 +7916,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")) + ', <a href=# onclick="return p5clearClip()" style=cursor:pointer>' + "Clear" + '</a>.' } QH('p5bottomstatus', x); p5setActions(); }
@ -8341,8 +8381,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; }
@ -8609,8 +8650,8 @@
function p30showUserChangePassDialog(multiFactor) {
if (xxdialogMode) return;
var x = '';
x += addHtmlValue("Password", '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
x += addHtmlValue("Password", '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
x += addHtmlValue("Password", '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=p30showUserChangePassDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
x += addHtmlValue("Password", '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=p30showUserChangePassDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
if (features & 0x00010000) { x += addHtmlValue("Password hint", '<input id=p4hint type=text style=width:230px maxlength=256></input>'); }
if (passRequirements) {

File diff suppressed because one or more lines are too long

View File

@ -12,13 +12,14 @@
</head>
<body style="font-family:Arial,Helvetica,sans-serif">
<div id="xtop" style="position:absolute;left:0;right:0;top:0;height:38px;background-color:#036;color:#c8c8c8;box-shadow:3px 3px 10px gray">
<div id="notifyButton" class="icon13 topButton" style="margin-right:4px;display:none" title="Enable browser notification" onclick="enableNotificationsButtonClick()"></div>
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
<div id="camButton" class="icon2 topButton" title="Activate camera & microphone" style="display:none" onclick="camButtonClick()"></div>
<div id="micButton" class="icon6 topButton" title="Activate microphone" style="display:none" onclick="micButtonClick()"></div>
<div id="hangupButton" class="icon11 topRedButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
<div style="display:inline-block;width:2px"></div>
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b>MeshMessenger<span id="xtitle"></span></b></div>
<div style="position:absolute;background-color:#036;right:0;height:38px">
<div id="notifyButton" class="icon13 topButton" style="margin-right:4px;display:none" title="Enable browser notification" onclick="enableNotificationsButtonClick()"></div>
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
<div id="camButton" class="icon2 topButton" title="Activate camera & microphone" style="display:none" onclick="camButtonClick()"></div>
<div id="micButton" class="icon6 topButton" title="Activate microphone" style="display:none" onclick="micButtonClick()"></div>
<div id="hangupButton" class="icon11 topRedButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
</div>
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b><span id="xtitle">MeshMessenger</span></b></div>
</div>
<div id="xmiddle" style="position:absolute;left:0;right:0;top:38px;bottom:30px">
<div style="position:absolute;left:0;right:0;top:0;bottom:0;overflow-y:scroll">
@ -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('&nbsp')); document.title = document.title + ' - ' + args.title; }
// Setup web notifications
if (Notification) { QV('notifyButton', Notification.permission != 'granted'); }

File diff suppressed because one or more lines are too long

View File

@ -500,7 +500,7 @@
<span id="DeskTimer" title="Session time"></span>&nbsp;
<select id="termdisplays" style="display:none" onchange="deskSetDisplay(event)" onkeypress="return false" onkeydown="return false"></select>&nbsp;
<input id="DeskToolsButton" type="button" value="Outils" title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()">&nbsp;
<span id="DeskChatButton" class="deskarea" title="Ouvrir la fenêtre de discussion sur cet ordinateur"><img src="images/icon-chat.png" onclick="deviceChat()" height="16" width="16" style="padding-top:2px"></span>
<span id="DeskChatButton" class="deskarea" title="Ouvrir la fenêtre de discussion sur cet ordinateur"><img src="images/icon-chat.png" onclick="deviceChat(event)" height="16" width="16" style="padding-top:2px"></span>
<span id="DeskNotifyButton" title="Display a notification on the remote computer"><img src="images/icon-notify.png" onclick="deviceToastFunction()" height="16" width="16" style="padding-top:2px"></span>
<span id="DeskOpenWebButton" title="Open a web address on remote computer"><img src="images/icon-url2.png" onclick="deviceUrlFunction()" height="16" width="16" style="padding-top:2px"></span>
</div>
@ -4500,11 +4500,15 @@
function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); }
function deviceChat() {
function deviceChat(e) {
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);
if (e && (e.shiftKey == true)) {
window.open(url, 'meshmessenger:' + currentNode._id);
} else {
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) });
}
@ -6143,6 +6147,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, '<input type=file name=files id=p13uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p13uploadinput\')" />'); 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');
@ -7719,12 +7724,12 @@
var h = '';
if (f.t < 3 || f.t == 4) {
var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = '';
h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + name + '\'>&nbsp;<span style=float:right title=\"' + title + '\">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p5folderset(\"' + encodeURIComponent(f.nx) + '\")></div><a href=# style=cursor:pointer onclick=\'return p5folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + name + '">&nbsp;<span style=float:right title=\"' + title + '\">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p5folderset(\"' + encodeURIComponent(f.nx) + '\")></div><a href=# style=cursor:pointer onclick=\'return p5folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
} else {
var link = shortname, publiclink = '';
if (publicfolder) { publiclink = ' (<a style=cursor:pointer title=\"Display public link\" onclick=\'return p5showPublicLink(\"' + publicPath + '/' + f.nx + '\")\'>' + "Link" + '</a>)'; }
if (f.s > 0) { link = '<a rel=\"noreferrer noopener\" target=\"_blank\" download href=\"downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '\">' + shortname + '</a>' + publiclink; }
h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + f.nx + '\'>&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
if (publicfolder) { publiclink = ' (<a style=cursor:pointer title="Display public link" onclick=\'return p5showPublicLink("' + publicPath + '/' + f.nx + '")\'>' + "Link" + '</a>)'; }
if (f.s > 0) { link = '<a rel="noreferrer noopener" target="_blank" download href="downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '">' + shortname + '</a>' + publiclink; }
h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + f.nx + '">&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
}
if (f.t < 3) { html1 += h; } else { html2 += h; }
@ -7820,9 +7825,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, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value=\"' + encodeURIComponent(filetreelinkpath) + '\" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /></form>'); updateUploadDialogOk('p5uploadinput'); }
function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value=\"' + encodeURIComponent(filetreelinkpath) + '\" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="p5updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /><span id=p5confirmOverwriteSpan style=display:none><br /><label><input type=checkbox id=p5confirmOverwrite onchange="p5updateUploadDialogOk(\'p5uploadinput\')" />' + "Confirm overwrite?" + '</label></span></form>'); 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');
@ -7836,7 +7867,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")) + ', <a href=# onclick="return p5clearClip()" style=cursor:pointer>' + "Clear" + '</a>.' } QH('p5bottomstatus', x); p5setActions(); }
@ -8292,8 +8332,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 +8601,8 @@
function p30showUserChangePassDialog(multiFactor) {
if (xxdialogMode) return;
var x = '';
x += addHtmlValue("Mot de passe", '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
x += addHtmlValue("Mot de passe", '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
x += addHtmlValue("Mot de passe", '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=p30showUserChangePassDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
x += addHtmlValue("Mot de passe", '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=p30showUserChangePassDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
if (features & 0x00010000) { x += addHtmlValue("Password hint", '<input id=p4hint type=text style=width:230px maxlength=256></input>'); }
if (passRequirements) {

File diff suppressed because one or more lines are too long

View File

@ -10,13 +10,14 @@
</head>
<body style="font-family:Arial,Helvetica,sans-serif">
<div id="xtop" style="position:absolute;left:0;right:0;top:0;height:38px;background-color:#036;color:#c8c8c8;box-shadow:3px 3px 10px gray">
<div id="notifyButton" class="icon13 topButton" style="margin-right:4px;display:none" title="Enable browser notification" onclick="enableNotificationsButtonClick()"></div>
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
<div id="camButton" class="icon2 topButton" title="Activer caméra et microphone" style="display:none" onclick="camButtonClick()"></div>
<div id="micButton" class="icon6 topButton" title="Activer le microphone" style="display:none" onclick="micButtonClick()"></div>
<div id="hangupButton" class="icon11 topRedButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
<div style="display:inline-block;width:2px"></div>
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b>MeshMessenger<span id="xtitle"></span></b></div>
<div style="position:absolute;background-color:#036;right:0;height:38px">
<div id="notifyButton" class="icon13 topButton" style="margin-right:4px;display:none" title="Enable browser notification" onclick="enableNotificationsButtonClick()"></div>
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
<div id="camButton" class="icon2 topButton" title="Activer caméra et microphone" style="display:none" onclick="camButtonClick()"></div>
<div id="micButton" class="icon6 topButton" title="Activer le microphone" style="display:none" onclick="micButtonClick()"></div>
<div id="hangupButton" class="icon11 topRedButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
</div>
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b><span id="xtitle">MeshMessenger</span></b></div>
</div>
<div id="xmiddle" style="position:absolute;left:0;right:0;top:38px;bottom:30px">
<div style="position:absolute;left:0;right:0;top:0;bottom:0;overflow-y:scroll">
@ -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('&nbsp')); document.title = document.title + ' - ' + args.title; }
// Setup web notifications
if (Notification) { QV('notifyButton', Notification.permission != 'granted'); }

View File

@ -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; });
@ -2690,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