diff --git a/MeshCentralServer.njsproj b/MeshCentralServer.njsproj
index d2d0cf3f..bc357bcb 100644
--- a/MeshCentralServer.njsproj
+++ b/MeshCentralServer.njsproj
@@ -170,6 +170,7 @@
+
diff --git a/agents/meshcore.js b/agents/meshcore.js
index 87578b1f..ba7b9874 100644
--- a/agents/meshcore.js
+++ b/agents/meshcore.js
@@ -1102,8 +1102,7 @@ function createMeshCore(agent) {
if (fs.existsSync("/usr/bin/python") && fs.existsSync("/bin/bash")) {
this.httprequest.process = childProcess.execFile("/usr/bin/python", [ "python", "-c", "import pty; pty.spawn([\"/bin/bash\"])" ]);
if (process.platform == 'linux') { this.httprequest.process.stdin.write("export TERM='xterm'\nalias ls='ls --color=auto'\nclear\n"); }
- }
- else if (fs.existsSync("/bin/bash")) {
+ } else if (fs.existsSync("/bin/bash")) {
this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
} else {
diff --git a/agents/meshcore.min.js b/agents/meshcore.min.js
index 87578b1f..ba7b9874 100644
--- a/agents/meshcore.min.js
+++ b/agents/meshcore.min.js
@@ -1102,8 +1102,7 @@ function createMeshCore(agent) {
if (fs.existsSync("/usr/bin/python") && fs.existsSync("/bin/bash")) {
this.httprequest.process = childProcess.execFile("/usr/bin/python", [ "python", "-c", "import pty; pty.spawn([\"/bin/bash\"])" ]);
if (process.platform == 'linux') { this.httprequest.process.stdin.write("export TERM='xterm'\nalias ls='ls --color=auto'\nclear\n"); }
- }
- else if (fs.existsSync("/bin/bash")) {
+ } else if (fs.existsSync("/bin/bash")) {
this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
} else {
diff --git a/db.js b/db.js
index 07a0e3f0..fec1c3d9 100644
--- a/db.js
+++ b/db.js
@@ -442,9 +442,9 @@ module.exports.CreateDB = function (parent, func) {
});
}
});
-
+
// Setup plugin info collection
- obj.pluginsfile = db.collection('plugins');
+ if (parent.config.settings != null) { obj.pluginsfile = db.collection('plugins'); }
setupFunctions(func); // Completed setup of MongoDB
});
@@ -548,7 +548,7 @@ module.exports.CreateDB = function (parent, func) {
});
// Setup plugin info collection
- obj.pluginsfile = db.collection('plugins');
+ if (parent.config.settings != null) { obj.pluginsfile = db.collection('plugins'); }
setupFunctions(func); // Completed setup of MongoJS
} else {
@@ -611,8 +611,10 @@ module.exports.CreateDB = function (parent, func) {
obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events
// Setup plugin info collection
- obj.pluginsfile = new Datastore({ filename: parent.getConfigFilePath('meshcentral-plugins.db'), autoload: true });
- obj.pluginsfile.persistence.setAutocompactionInterval(36000);
+ if (parent.config.settings != null) {
+ obj.pluginsfile = new Datastore({ filename: parent.getConfigFilePath('meshcentral-plugins.db'), autoload: true });
+ obj.pluginsfile.persistence.setAutocompactionInterval(36000);
+ }
setupFunctions(func); // Completed setup of NeDB
}
@@ -764,22 +766,16 @@ module.exports.CreateDB = function (parent, func) {
func(r);
});
}
-
- // Add a plugin
- obj.addPlugin = function (plugin, func) { plugin.type = "plugin"; obj.pluginsfile.insertOne(plugin, func); };
-
- // Get all plugins
- obj.getPlugins = function (func) { obj.pluginsfile.find({"type": "plugin"}).project({"type": 0}).sort({ name: 1 }).toArray(func); };
-
- // Get plugin
- obj.getPlugin = function (id, func) { id = require('mongodb').ObjectID(id); obj.pluginsfile.find({ _id: id }).sort({ name: 1 }).toArray(func); };
-
- // Delete plugin
- obj.deletePlugin = function (id, func) { id = require('mongodb').ObjectID(id); obj.pluginsfile.deleteOne({ _id: id }, func); };
-
- obj.setPluginStatus = function(id, status, func) { id = require('mongodb').ObjectID(id); obj.pluginsfile.updateOne({ _id: id }, { $set: {status: status } }, func); };
-
- obj.updatePlugin = function(id, args, func) { delete args._id; id = require('mongodb').ObjectID(id); obj.pluginsfile.updateOne({ _id: id }, { $set: args }, func); };
+
+ // Plugin operations
+ if (parent.config.settings.plugins != null) {
+ obj.addPlugin = function (plugin, func) { plugin.type = "plugin"; obj.pluginsfile.insertOne(plugin, func); }; // Add a plugin
+ obj.getPlugins = function (func) { obj.pluginsfile.find({ "type": "plugin" }).project({ "type": 0 }).sort({ name: 1 }).toArray(func); }; // Get all plugins
+ obj.getPlugin = function (id, func) { id = require('mongodb').ObjectID(id); obj.pluginsfile.find({ _id: id }).sort({ name: 1 }).toArray(func); }; // Get plugin
+ obj.deletePlugin = function (id, func) { id = require('mongodb').ObjectID(id); obj.pluginsfile.deleteOne({ _id: id }, func); }; // Delete plugin
+ obj.setPluginStatus = function (id, status, func) { id = require('mongodb').ObjectID(id); obj.pluginsfile.updateOne({ _id: id }, { $set: { status: status } }, func); };
+ obj.updatePlugin = function (id, args, func) { delete args._id; id = require('mongodb').ObjectID(id); obj.pluginsfile.updateOne({ _id: id }, { $set: args }, func); };
+ }
} else {
// Database actions on the main collection (NeDB and MongoJS)
@@ -911,22 +907,16 @@ module.exports.CreateDB = function (parent, func) {
func(r);
});
}
-
- // Add a plugin
- obj.addPlugin = function (plugin, func) { plugin.type = "plugin"; obj.pluginsfile.insert(plugin, func); };
-
- // Get all plugins
- obj.getPlugins = function (func) { obj.pluginsfile.find({"type": "plugin"}, {"type": 0}).sort({ name: 1 }).exec(func); };
-
- // Get plugin
- obj.getPlugin = function (id, func) { obj.pluginsfile.find({ _id: id }).sort({ name: 1 }).exec(func); };
-
- // Delete plugin
- obj.deletePlugin = function (id, func) { obj.pluginsfile.remove({ _id: id }, func); };
-
- obj.setPluginStatus = function(id, status, func) { obj.pluginsfile.update({ _id: id }, { $set: {status: status } }, func); };
-
- obj.updatePlugin = function(id, args, func) { delete args._id; obj.pluginsfile.update({ _id: id }, { $set: args }, func); };
+
+ // Plugin operations
+ if (parent.config.settings.plugins != null) {
+ obj.addPlugin = function (plugin, func) { plugin.type = "plugin"; obj.pluginsfile.insert(plugin, func); }; // Add a plugin
+ obj.getPlugins = function (func) { obj.pluginsfile.find({ "type": "plugin" }, { "type": 0 }).sort({ name: 1 }).exec(func); }; // Get all plugins
+ obj.getPlugin = function (id, func) { obj.pluginsfile.find({ _id: id }).sort({ name: 1 }).exec(func); }; // Get plugin
+ obj.deletePlugin = function (id, func) { obj.pluginsfile.remove({ _id: id }, func); }; // Delete plugin
+ obj.setPluginStatus = function (id, status, func) { obj.pluginsfile.update({ _id: id }, { $set: { status: status } }, func); };
+ obj.updatePlugin = function (id, args, func) { delete args._id; obj.pluginsfile.update({ _id: id }, { $set: args }, func); };
+ }
}
diff --git a/meshcentral.js b/meshcentral.js
index 3fe365bf..df75489c 100644
--- a/meshcentral.js
+++ b/meshcentral.js
@@ -840,7 +840,13 @@ 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);
+ const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
+ if (nodeVersion < 7) {
+ console.log("WARNING: Plugin support requires Node 7 or higher.");
+ delete obj.config.settings.plugins;
+ } else {
+ obj.pluginHandler = require('./pluginHandler.js').pluginHandler(obj);
+ }
}
// Load the default meshcore and meshcmd
@@ -2073,6 +2079,7 @@ function mainStart() {
if (config.settings.mqtt != null) { modules.push('aedes'); } // Add MQTT Modules
if (config.settings.mongodb != null) { modules.push('mongodb'); } // Add MongoDB, official driver.
if (config.settings.vault != null) { modules.push('node-vault'); } // Add official HashiCorp's Vault module.
+ if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent'); } // Required for HTTP/HTTPS proxy support
else if (config.settings.xmongodb != null) { modules.push('mongojs'); } // Add MongoJS, old driver.
if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support
diff --git a/meshuser.js b/meshuser.js
index a4780c80..952f8862 100644
--- a/meshuser.js
+++ b/meshuser.js
@@ -3138,6 +3138,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
break;
}
case 'distributeCore': {
+ // This is only available when plugins are enabled since it could cause stress on the server
+ if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin with plugins enabled
for (var i in command.nodes) {
parent.sendMeshAgentCore(user, domain, command.nodes[i]._id, 'default');
}
@@ -3170,7 +3172,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
})
.catch(function(err) {
if (typeof err == 'object') err = err.message;
- try { ws.send(JSON.stringify({ action: 'pluginError', msg: err })); } catch (er) { }
+ try { ws.send(JSON.stringify({ action: 'pluginError', msg: err })); } catch (er) { }
});
} catch(e) { console.log('Cannot add plugin: ' + e); }
diff --git a/pluginHandler.js b/pluginHandler.js
index 0c70e55d..d88cd980 100644
--- a/pluginHandler.js
+++ b/pluginHandler.js
@@ -25,27 +25,27 @@ module.exports.pluginHandler = function (parent) {
obj.plugins = {};
obj.exports = {};
obj.loadList = obj.parent.config.settings.plugins.list; // For local development / manual install, not from DB
-
+
if (typeof obj.loadList != 'object') {
obj.loadList = {};
- parent.db.getPlugins(function(err, plugins){
- plugins.forEach(function(plugin){
- if (plugin.status != 1) return;
- if (obj.fs.existsSync(obj.pluginPath + '/' + plugin.shortName)) {
- try {
- obj.plugins[plugin.shortName] = require(obj.pluginPath + '/' + plugin.shortName + '/' + plugin.shortName + '.js')[plugin.shortName](obj);
- obj.exports[plugin.shortName] = obj.plugins[plugin.shortName].exports;
- } catch (e) {
- console.log("Error loading plugin: " + plugin.shortName + " (" + e + "). It has been disabled.", e.stack);
- }
- try { // try loading local info about plugin to database (if it changed locally)
- var plugin_config = obj.fs.readFileSync(obj.pluginPath + '/' + plugin.shortName + '/config.json');
- plugin_config = JSON.parse(plugin_config);
- parent.db.updatePlugin(plugin._id, plugin_config);
- } catch (e) { console.log('Plugin config file for '+ plugin.name +' could not be parsed.'); }
- }
- obj.parent.updateMeshCore(); // db calls are delayed, lets inject here once we're ready
- });
+ parent.db.getPlugins(function (err, plugins) {
+ plugins.forEach(function (plugin) {
+ if (plugin.status != 1) return;
+ if (obj.fs.existsSync(obj.pluginPath + '/' + plugin.shortName)) {
+ try {
+ obj.plugins[plugin.shortName] = require(obj.pluginPath + '/' + plugin.shortName + '/' + plugin.shortName + '.js')[plugin.shortName](obj);
+ obj.exports[plugin.shortName] = obj.plugins[plugin.shortName].exports;
+ } catch (e) {
+ console.log("Error loading plugin: " + plugin.shortName + " (" + e + "). It has been disabled.", e.stack);
+ }
+ try { // try loading local info about plugin to database (if it changed locally)
+ var plugin_config = obj.fs.readFileSync(obj.pluginPath + '/' + plugin.shortName + '/config.json');
+ plugin_config = JSON.parse(plugin_config);
+ parent.db.updatePlugin(plugin._id, plugin_config);
+ } catch (e) { console.log("Plugin config file for " + plugin.name + " could not be parsed."); }
+ }
+ obj.parent.updateMeshCore(); // db calls are delayed, lets inject here once we're ready
+ });
});
} else {
obj.loadList.forEach(function (plugin, index) {
@@ -59,15 +59,15 @@ module.exports.pluginHandler = function (parent) {
}
});
}
-
+
obj.prepExports = function () {
var str = 'function() {\r\n';
str += ' var obj = {};\r\n';
- for (const p of Object.keys(obj.plugins)) {
+ for (var p of Object.keys(obj.plugins)) {
str += ' obj.' + p + ' = {};\r\n';
if (Array.isArray(obj.exports[p])) {
- for (const l of Object.values(obj.exports[p])) {
+ for (var l of Object.values(obj.exports[p])) {
str += ' obj.' + p + '.' + l + ' = ' + obj.plugins[p][l].toString() + '\r\n';
}
}
@@ -97,7 +97,7 @@ module.exports.pluginHandler = function (parent) {
meshserver.send({ action: 'addplugin', url: Q('pluginurlinput').value});
};
obj.addPluginDlg = function() {
- setDialogMode(2, "Plugin Config URL", 3, obj.addPluginEx, ' ');
+ setDialogMode(2, "Plugin Download URL", 3, obj.addPluginEx, '
WARNING: Downloading plugins may compromise server security. Only download from trusted sources.
');
focusTextBox('pluginurlinput');
};
obj.refreshPluginHandler = function() {
@@ -108,20 +108,20 @@ module.exports.pluginHandler = function (parent) {
return obj; };`;
return str;
}
-
- obj.refreshJS = function(req, res) {
+
+ obj.refreshJS = function (req, res) {
// to minimize server reboots when installing new plugins, we call the new data and overwrite the old pluginHandler on the front end
res.set('Content-Type', 'text/javascript');
- res.send('pluginHandlerBuilder = '+obj.prepExports() + ' pluginHandler = new pluginHandlerBuilder();');
+ res.send('pluginHandlerBuilder = ' + obj.prepExports() + ' pluginHandler = new pluginHandlerBuilder();');
}
-
+
obj.callHook = function (hookName, ...args) {
for (var p in obj.plugins) {
if (typeof obj.plugins[p][hookName] == 'function') {
try {
obj.plugins[p][hookName](args);
} catch (e) {
- console.log('Error ocurred while running plugin hook' + p + ':' + hookName + ' (' + e + ')');
+ console.log("Error ocurred while running plugin hook" + p + ':' + hookName + ' (' + e + ')');
}
}
}
@@ -187,20 +187,20 @@ module.exports.pluginHandler = function (parent) {
panel[p].header = obj.plugins[p].on_device_header();
panel[p].content = obj.plugins[p].on_device_page();
} catch (e) {
- console.log('Error ocurred while getting plugin views ' + p + ':' + ' (' + e + ')');
+ console.log("Error ocurred while getting plugin views " + p + ':' + ' (' + e + ')');
}
}
}
return panel;
};
-
- obj.isValidConfig = function(conf, url) { // check for the required attributes
+
+ obj.isValidConfig = function (conf, url) { // check for the required attributes
var isValid = true;
if (!(
typeof conf.name == 'string'
&& typeof conf.shortName == 'string'
&& typeof conf.version == 'string'
- // && typeof conf.author == 'string'
+ // && typeof conf.author == 'string'
&& typeof conf.description == 'string'
&& typeof conf.hasAdminPanel == 'boolean'
&& typeof conf.homepage == 'string'
@@ -210,7 +210,7 @@ module.exports.pluginHandler = function (parent) {
&& typeof conf.repository.type == 'string'
&& typeof conf.repository.url == 'string'
&& typeof conf.meshCentralCompat == 'string'
- // && conf.configUrl == url // make sure we're loading a plugin from its desired config
+ // && conf.configUrl == url // make sure we're loading a plugin from its desired config
)) isValid = false;
// more checks here?
if (conf.repository.type == 'git') {
@@ -218,119 +218,120 @@ module.exports.pluginHandler = function (parent) {
}
return isValid;
};
-
- obj.getPluginConfig = function(configUrl) {
- return new Promise(function(resolve, reject) {
- if (configUrl.indexOf('https://') >= 0) {
- var http = require('https');
- } else {
- var http = require('http');
+
+ // https://raw.githubusercontent.com/ryanblenis/MeshCentral-Sample/master/config.json
+ obj.getPluginConfig = function (configUrl) {
+ return new Promise(function (resolve, reject) {
+ var http = (configUrl.indexOf('https://') >= 0) ? require('https') : require('http');
+ if (configUrl.indexOf('://') === -1) reject("Unable to fetch the config: Bad URL (" + configUrl + ")");
+ var options = require('url').parse(configUrl);
+ if (typeof parent.config.settings.plugins.proxy == 'string') { // Proxy support
+ const HttpsProxyAgent = require('https-proxy-agent');
+ options.agent = new HttpsProxyAgent(require('url').parse(parent.config.settings.plugins.proxy));
}
- if (configUrl.indexOf('://') === -1) reject('Unable to fetch the config: Bad URL (' + configUrl + ')');
- http.get(configUrl, function(res) {
- var configStr = '';
- res.on('data', function(chunk){
- configStr += chunk;
- });
- res.on('end', function(){
- if (configStr[0] == '{') { // let's be sure we're JSON
- try {
- var pluginConfig = JSON.parse(configStr);
- if (Array.isArray(pluginConfig) && pluginConfig.length == 1) pluginConfig = pluginConfig[0];
- if (obj.isValidConfig(pluginConfig, configUrl)) {
- resolve(pluginConfig);
- } else {
- reject("This does not appear to be a valid plugin configuration.");
- }
-
- } catch (e) { reject('Error getting plugin config. Check that you have valid JSON.'); }
- } else {
- reject('Error getting plugin config. Check that you have valid JSON.');
- }
- });
-
- }).on('error', function(e) {
+ http.get(options, function (res) {
+ var configStr = '';
+ res.on('data', function (chunk) {
+ configStr += chunk;
+ });
+ res.on('end', function () {
+ if (configStr[0] == '{') { // Let's be sure we're JSON
+ try {
+ var pluginConfig = JSON.parse(configStr);
+ if (Array.isArray(pluginConfig) && pluginConfig.length == 1) pluginConfig = pluginConfig[0];
+ if (obj.isValidConfig(pluginConfig, configUrl)) {
+ resolve(pluginConfig);
+ } else {
+ reject("This does not appear to be a valid plugin configuration.");
+ }
+ } catch (e) { reject("Error getting plugin config. Check that you have valid JSON."); }
+ } else {
+ reject("Error getting plugin config. Check that you have valid JSON.");
+ }
+ });
+
+ }).on('error', function (e) {
reject("Error getting plugin config: " + e.message);
- });
+ });
})
};
-
- obj.getPluginLatest = function() {
- return new Promise(function(resolve, reject) {
- parent.db.getPlugins(function(err, plugins) {
+
+ obj.getPluginLatest = function () {
+ return new Promise(function (resolve, reject) {
+ parent.db.getPlugins(function (err, plugins) {
var proms = [];
- plugins.forEach(function(curconf) {
- proms.push(obj.getPluginConfig(curconf.configUrl).catch(e => { return null; } ));
+ plugins.forEach(function (curconf) {
+ proms.push(obj.getPluginConfig(curconf.configUrl).catch(e => { return null; }));
});
var latestRet = [];
- Promise.all(proms).then(function(newconfs) {
+ Promise.all(proms).then(function (newconfs) {
var nconfs = [];
- // filter out config download issues
- newconfs.forEach(function(nc) {
- if (nc !== null) nconfs.push(nc);
- });
- nconfs.forEach(function(newconf) {
- var curconf = null;
- plugins.forEach(function(conf) {
- if (conf.configUrl == newconf.configUrl) curconf = conf;
+ // Filter out config download issues
+ newconfs.forEach(function (nc) { if (nc !== null) nconfs.push(nc); });
+ if (nconfs.length == 0) { resolve([]); } else {
+ nconfs.forEach(function (newconf) {
+ var curconf = null;
+ plugins.forEach(function (conf) {
+ if (conf.configUrl == newconf.configUrl) curconf = conf;
+ });
+ if (curconf == null) reject("Some plugin configs could not be parsed");
+ var s = require('semver');
+ // MeshCentral doesn't adhere to semantic versioning (due to the - at the end of the version)
+ // Convert the letter to ASCII for a "true" version number comparison
+ var mcCurVer = parent.currentVer.replace(/-(.)$/, (m, p1) => { return p1.charCodeAt(0); });
+ var piCompatVer = newconf.meshCentralCompat.replace(/-(.)\b/g, (m, p1) => { return p1.charCodeAt(0); });
+ latestRet.push({
+ 'id': curconf._id,
+ 'installedVersion': curconf.version,
+ 'version': newconf.version,
+ 'hasUpdate': s.gt(newconf.version, curconf.version),
+ 'meshCentralCompat': s.satisfies(mcCurVer, piCompatVer),
+ 'changelogUrl': curconf.changelogUrl,
+ 'status': curconf.status
+ });
+ resolve(latestRet);
});
- if (curconf == null) reject('Some plugin configs could not be parsed');
- var s = require('semver');
- // MeshCentral doesn't adhere to semantic versioning (due to the - at the end of the version)
- // Convert the letter to ASCII for a "true" version number comparison
- var mcCurVer = parent.currentVer.replace(/-(.)$/, (m, p1) => { return p1.charCodeAt(0); });
- var piCompatVer = newconf.meshCentralCompat.replace(/-(.)\b/g, (m, p1) => { return p1.charCodeAt(0); });
- latestRet.push({
- "id": curconf._id,
- "installedVersion": curconf.version,
- "version": newconf.version,
- "hasUpdate": s.gt(newconf.version, curconf.version),
- "meshCentralCompat": s.satisfies(mcCurVer, piCompatVer),
- "changelogUrl": curconf.changelogUrl,
- "status": curconf.status
- });
- resolve(latestRet);
- });
- }).catch((e) => { console.log('Error reaching plugins, update call aborted. ', e)});
+ }
+ }).catch((e) => { console.log("Error reaching plugins, update call aborted.", e) });
});
});
};
-
- obj.addPlugin = function(pluginConfig) {
- return new Promise(function(resolve, reject) {
- parent.db.addPlugin({
- "name": pluginConfig.name,
- "shortName": pluginConfig.shortName,
- "version": pluginConfig.version,
- "description": pluginConfig.description,
- "hasAdminPanel": pluginConfig.hasAdminPanel,
- "homepage": pluginConfig.homepage,
- "changelogUrl": pluginConfig.changelogUrl,
- "configUrl": pluginConfig.configUrl,
- "downloadUrl": pluginConfig.downloadUrl,
- "repository": {
- "type": pluginConfig.repository.type,
- "url": pluginConfig.repository.url
- },
- "meshCentralCompat": pluginConfig.meshCentralCompat,
- "versionHistoryUrl": pluginConfig.versionHistoryUrl,
- "status": 0 // 0: disabled, 1: enabled
- }, function() {
- parent.db.getPlugins(function(err, docs){
- if (err) reject(err);
- else resolve(docs);
+
+ obj.addPlugin = function (pluginConfig) {
+ return new Promise(function (resolve, reject) {
+ parent.db.addPlugin({
+ 'name': pluginConfig.name,
+ 'shortName': pluginConfig.shortName,
+ 'version': pluginConfig.version,
+ 'description': pluginConfig.description,
+ 'hasAdminPanel': pluginConfig.hasAdminPanel,
+ 'homepage': pluginConfig.homepage,
+ 'changelogUrl': pluginConfig.changelogUrl,
+ 'configUrl': pluginConfig.configUrl,
+ 'downloadUrl': pluginConfig.downloadUrl,
+ 'repository': {
+ 'type': pluginConfig.repository.type,
+ 'url': pluginConfig.repository.url
+ },
+ 'meshCentralCompat': pluginConfig.meshCentralCompat,
+ 'versionHistoryUrl': pluginConfig.versionHistoryUrl,
+ 'status': 0 // 0: disabled, 1: enabled
+ }, function () {
+ parent.db.getPlugins(function (err, docs) {
+ if (err) reject(err);
+ else resolve(docs);
});
});
});
};
-
- obj.installPlugin = function(id, version_only, force_url, func) {
- parent.db.getPlugin(id, function(err, docs){
+
+ obj.installPlugin = function (id, version_only, force_url, func) {
+ parent.db.getPlugin(id, function (err, docs) {
// the "id" would probably suffice, but is probably an sanitary issue, generate a random instead
var randId = Math.random().toString(32).replace('0.', '');
- var fileName = obj.parent.path.join(require('os').tmpdir(), 'Plugin_'+randId+'.zip');
+ var fileName = obj.parent.path.join(require('os').tmpdir(), 'Plugin_' + randId + '.zip');
var plugin = docs[0];
- if (plugin.repository.type == 'git') {
+ if (plugin.repository.type == 'git') {
const file = obj.fs.createWriteStream(fileName);
var dl_url = plugin.downloadUrl;
if (version_only != null && version_only != false) dl_url = version_only.url;
@@ -339,7 +340,7 @@ module.exports.pluginHandler = function (parent) {
var q = url.parse(dl_url, true);
var http = (q.protocol == "http") ? require('http') : require('https');
var opts = {
- path: q.pathname,
+ path: q.pathname,
host: q.hostname,
port: q.port,
headers: {
@@ -348,12 +349,16 @@ module.exports.pluginHandler = function (parent) {
followRedirects: true,
method: 'GET'
};
- var request = http.get(opts, function(response) {
+ if (typeof parent.config.settings.plugins.proxy == 'string') { // Proxy support
+ const HttpsProxyAgent = require('https-proxy-agent');
+ opts.agent = new HttpsProxyAgent(require('url').parse(parent.config.settings.plugins.proxy));
+ }
+ var request = http.get(opts, function (response) {
// handle redirections with grace
if (response.headers.location) return obj.installPlugin(id, version_only, response.headers.location, func);
response.pipe(file);
- file.on('finish', function() {
- file.close(function(){
+ file.on('finish', function () {
+ file.close(function () {
var yauzl = require("yauzl");
if (!obj.fs.existsSync(obj.pluginPath)) {
obj.fs.mkdirSync(obj.pluginPath);
@@ -364,12 +369,12 @@ module.exports.pluginHandler = function (parent) {
yauzl.open(fileName, { lazyEntries: true }, function (err, zipfile) {
if (err) throw err;
zipfile.readEntry();
- zipfile.on("entry", function (entry) {
+ zipfile.on('entry', function (entry) {
let pluginPath = obj.parent.path.join(obj.pluginPath, plugin.shortName);
let pathReg = new RegExp(/(.*?\/)/);
- if (process.platform == 'win32') pathReg = new RegExp(/(.*?\\/);
+ //if (process.platform == 'win32') { pathReg = new RegExp(/(.*?\\/); }
let filePath = obj.parent.path.join(pluginPath, entry.fileName.replace(pathReg, '')); // remove top level dir
-
+
if (/\/$/.test(entry.fileName)) { // dir
if (!obj.fs.existsSync(filePath))
obj.fs.mkdirSync(filePath);
@@ -377,50 +382,48 @@ module.exports.pluginHandler = function (parent) {
} else { // file
zipfile.openReadStream(entry, function (err, readStream) {
if (err) throw err;
- readStream.on("end", function () { zipfile.readEntry(); });
+ readStream.on('end', function () { zipfile.readEntry(); });
readStream.pipe(obj.fs.createWriteStream(filePath));
});
}
});
- zipfile.on("end", function () { setTimeout(function () {
- obj.fs.unlinkSync(fileName);
- if (version_only == null || version_only === false) {
- parent.db.setPluginStatus(id, 1, func);
- } else {
- parent.db.updatePlugin(id, { status: 1, version: version_only.name }, func);
- }
- obj.plugins[plugin.shortName] = require(obj.pluginPath + '/' + plugin.shortName + '/' + plugin.shortName + '.js')[plugin.shortName](obj);
- obj.exports[plugin.shortName] = obj.plugins[plugin.shortName].exports;
- if (typeof obj.plugins[plugin.shortName].server_startup == 'function') obj.plugins[plugin.shortName].server_startup();
- var plugin_config = obj.fs.readFileSync(obj.pluginPath + '/' + plugin.shortName + '/config.json');
- plugin_config = JSON.parse(plugin_config);
- parent.db.updatePlugin(plugin._id, plugin_config);
- parent.updateMeshCore();
- }); });
+ zipfile.on('end', function () {
+ setTimeout(function () {
+ obj.fs.unlinkSync(fileName);
+ if (version_only == null || version_only === false) {
+ parent.db.setPluginStatus(id, 1, func);
+ } else {
+ parent.db.updatePlugin(id, { status: 1, version: version_only.name }, func);
+ }
+ obj.plugins[plugin.shortName] = require(obj.pluginPath + '/' + plugin.shortName + '/' + plugin.shortName + '.js')[plugin.shortName](obj);
+ obj.exports[plugin.shortName] = obj.plugins[plugin.shortName].exports;
+ if (typeof obj.plugins[plugin.shortName].server_startup == 'function') obj.plugins[plugin.shortName].server_startup();
+ var plugin_config = obj.fs.readFileSync(obj.pluginPath + '/' + plugin.shortName + '/config.json');
+ plugin_config = JSON.parse(plugin_config);
+ parent.db.updatePlugin(plugin._id, plugin_config);
+ parent.updateMeshCore();
+ });
+ });
});
});
});
});
- } else if (plugin.repository.type == 'npm') {
+ } else if (plugin.repository.type == 'npm') {
// @TODO npm support? (need a test plugin)
}
-
-
});
-
-
};
-
- obj.getPluginVersions = function(id) {
- return new Promise(function(resolve, reject) {
- parent.db.getPlugin(id, function(err, docs) {
+
+ obj.getPluginVersions = function (id) {
+ return new Promise(function (resolve, reject) {
+ parent.db.getPlugin(id, function (err, docs) {
var plugin = docs[0];
- if (plugin.versionHistoryUrl == null) reject('No version history available for this plugin.');
+ if (plugin.versionHistoryUrl == null) reject("No version history available for this plugin.");
var url = require('url');
var q = url.parse(plugin.versionHistoryUrl, true);
- var http = (q.protocol == "http") ? require('http') : require('https');
+ var http = (q.protocol == 'http') ? require('http') : require('https');
var opts = {
- path: q.pathname,
+ path: q.pathname,
host: q.hostname,
port: q.port,
headers: {
@@ -428,47 +431,51 @@ module.exports.pluginHandler = function (parent) {
'Accept': 'application/vnd.github.v3+json'
}
};
- http.get(opts, function(res) {
+ if (typeof parent.config.settings.plugins.proxy == 'string') { // Proxy support
+ const HttpsProxyAgent = require('https-proxy-agent');
+ options.agent = new HttpsProxyAgent(require('url').parse(parent.config.settings.plugins.proxy));
+ }
+ http.get(opts, function (res) {
var versStr = '';
- res.on('data', function(chunk){
+ res.on('data', function (chunk) {
versStr += chunk;
});
- res.on('end', function(){
+ res.on('end', function () {
if (versStr[0] == '{' || versStr[0] == '[') { // let's be sure we're JSON
try {
var vers = JSON.parse(versStr);
var vList = [];
var s = require('semver');
vers.forEach((v) => {
- if (s.lt(v.name, plugin.version)) vList.push(v);
+ if (s.lt(v.name, plugin.version)) vList.push(v);
});
- if (vers.length == 0) reject('No previous versions available.');
+ if (vers.length == 0) reject("No previous versions available.");
resolve({ 'id': plugin._id, 'name': plugin.name, versionList: vList });
- } catch (e) { reject('Version history problem.'); }
+ } catch (e) { reject("Version history problem."); }
} else {
- reject('Version history appears to be malformed.'+versStr);
+ reject("Version history appears to be malformed." + versStr);
}
});
- }).on('error', function(e) {
+ }).on('error', function (e) {
reject("Error getting plugin versions: " + e.message);
- });
+ });
});
});
};
-
- obj.disablePlugin = function(id, func) {
- parent.db.getPlugin(id, function(err, docs){
+
+ obj.disablePlugin = function (id, func) {
+ parent.db.getPlugin(id, function (err, docs) {
var plugin = docs[0];
parent.db.setPluginStatus(id, 0, func);
delete obj.plugins[plugin.shortName];
delete obj.exports[plugin.shortName];
});
};
-
- obj.removePlugin = function(id, func) {
- parent.db.getPlugin(id, function(err, docs){
+
+ obj.removePlugin = function (id, func) {
+ parent.db.getPlugin(id, function (err, docs) {
var plugin = docs[0];
- var rimraf = require("rimraf");
+ var rimraf = require('rimraf');
let pluginPath = obj.parent.path.join(obj.pluginPath, plugin.shortName);
rimraf.sync(pluginPath);
parent.db.deletePlugin(id, func);
@@ -476,25 +483,23 @@ module.exports.pluginHandler = function (parent) {
obj.parent.updateMeshCore();
});
};
-
+
obj.handleAdminReq = function (req, res, user, serv) {
var path = obj.path.join(obj.pluginPath, req.query.pin, 'views');
serv.app.set('views', path);
if (obj.plugins[req.query.pin] != null && typeof obj.plugins[req.query.pin].handleAdminReq == 'function') {
obj.plugins[req.query.pin].handleAdminReq(req, res, user);
- }
- else {
+ } else {
res.sendStatus(401);
}
}
-
- obj.handleAdminPostReq = function(req, res, user, serv) {
+
+ obj.handleAdminPostReq = function (req, res, user, serv) {
var path = obj.path.join(obj.pluginPath, req.query.pin, 'views');
serv.app.set('views', path);
if (obj.plugins[req.query.pin] != null && typeof obj.plugins[req.query.pin].handleAdminPostReq == 'function') {
obj.plugins[req.query.pin].handleAdminPostReq(req, res, user);
- }
- else {
+ } else {
res.sendStatus(401);
}
}
diff --git a/public/images/leftbar-64.png b/public/images/leftbar-64.png
index ef07b545..1299fed8 100644
Binary files a/public/images/leftbar-64.png and b/public/images/leftbar-64.png differ
diff --git a/public/images/plugin.png b/public/images/plugin.png
new file mode 100644
index 00000000..7f396383
Binary files /dev/null and b/public/images/plugin.png differ
diff --git a/public/images/plugin24.png b/public/images/plugin24.png
new file mode 100644
index 00000000..fc42c00e
Binary files /dev/null and b/public/images/plugin24.png differ
diff --git a/public/images/plus32.png b/public/images/plus32.png
deleted file mode 100644
index f2e17cce..00000000
Binary files a/public/images/plus32.png and /dev/null differ
diff --git a/public/styles/style.css b/public/styles/style.css
index e326a87e..d7b44da6 100644
--- a/public/styles/style.css
+++ b/public/styles/style.css
@@ -1295,17 +1295,6 @@ a {
left: 6px;
}
-.lb7 {
- background: url(../images/leftbar-64.png) -382px -2px;
- height: 62px;
- width: 62px;
- cursor: pointer;
- border: none;
- position: absolute;
- top: 6px;
- left: 6px;
-}
-
.m0 {
background: url(../images/images16.png) -32px 0px;
height: 16px;
@@ -1694,6 +1683,10 @@ a {
background-color: #FFFFFF;
}
+.gradTable1 { background-image: linear-gradient(to right, #fff 0%, #d3d9d6 100%); }
+.gradTable2 { background-color: #d3d9d6; }
+.gradTable3 { background-image: linear-gradient(to right, #d3d9d6 0%, #fff 100%); }
+
.h1 {
background-position: 0% 0%;
width: 14px;
@@ -2580,45 +2573,47 @@ a {
background-color: #DDD;
}
-#p7tbl {
+.p42tblRow {
+ height: 36px;
+ max-height: 40px;
+}
+
+#p42tbl {
width: 100%;
- border-collapse: collapse;
+ border-collapse: separate;
+ border-spacing:0 5px;
}
-#p7tbl th, #p7tbl td {
+#p42tbl th, #p7tbl td {
text-align: left;
- padding: 12px;
+ padding: 4px;
}
-#p7tbl tr:nth-child(n+2):nth-child(odd) {
- background-color: #cfeeff;
-}
-
-#p7tbl .chName {
+#p42tbl .chName {
width: 20%;
}
-#p7tbl .chDescription {
+#p42tbl .chDescription {
width: 38%;
}
-#p7tbl .chSite {
+#p42tbl .chSite {
width: 7%;
}
-#p7tbl .chVersion {
+#p42tbl .chVersion {
width: 5%;
}
-#p7tbl .chUpgradeAvail {
+#p42tbl .chUpgradeAvail {
width: 10%;
}
-#p7tbl .chStatus {
+#p42tbl .chStatus {
width: 10%;
}
-#p7tbl .chAction {
+#p42tbl .chAction {
width: 10%;
}
@@ -2636,12 +2631,14 @@ a {
}
#pluginRestartNotice {
+ /*
width: 40em;
font-weight: bold;
border: 1px solid red;
text-align: center;
padding: 14px;
margin: 50px auto;
+ */
}
.pluginContent {
diff --git a/views/default-min.handlebars b/views/default-min.handlebars
index 330c9d7f..1004a478 100644
--- a/views/default-min.handlebars
+++ b/views/default-min.handlebars
@@ -1,4 +1,4 @@
-{{{title}}}
My Devices My Account My Events My Files My Users My Server
General Desktop Terminal Files Events Details Intel® AMT Console Plugins
Server disconnected ,click to reconnect .
My Devices No device groups.
My Account Device Groups ( New )
My Events Show Last 60 Last 120 Last 250 Last 500 Last 1000
My Files These files are shared publicly, click "link" to get public url.
✓
✗
My Server Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Show Last 60 Last 120 Last 250 Last 500 Last 1000
General -
Events - Show Last 60 Last 120 Last 250 Last 500 Last 1000
File Selection
Local file upload Server file selection Agent Remote Desktop Scaling
100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Frame rate
Fast Medium Slow Very slow Intel® AMT Hardware KVM Image Encoding
RLE8, Fastest RLE16, Recommended RAW8, Slow RAW16, Very Slow {{{title}}}
My Devices My Account My Events My Files My Users My Server
General Desktop Terminal Files Events Details Intel® AMT Console Plugins
Server disconnected ,click to reconnect .
My Devices No device groups.
My Account Device Groups ( New )
My Events Show Last 60 Last 120 Last 250 Last 500 Last 1000
My Files These files are shared publicly, click "link" to get public url.
✓
✗
My Server Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Show Last 60 Last 120 Last 250 Last 500 Last 1000
General -
Events - Show Last 60 Last 120 Last 250 Last 500 Last 1000
My Server Plugins Name Description Link Version Latest Status Action
No plugins on server.
File Selection
Local file upload Server file selection Agent Remote Desktop Scaling
100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Frame rate
Fast Medium Slow Very slow Intel® AMT Hardware KVM Image Encoding
RLE8, Fastest RLE16, Recommended RAW8, Slow RAW16, Very Slow
diff --git a/views/translations/default-min_fr.handlebars b/views/translations/default-min_fr.handlebars
index dfe91c1a..0fee842c 100644
--- a/views/translations/default-min_fr.handlebars
+++ b/views/translations/default-min_fr.handlebars
@@ -1,4 +1,4 @@
-{{{title}}}
Mes Appareils Mon Compte Mes Événements Mes Dossiers Mes Utilisateurs Mon Serveur
General Desktop Terminal Files Events Details Intel® AMT Console Plugins
Server disconnected ,click to reconnect .
Mes Appareils Aucun groupe d'appareils.
Mon Compte Device Groups ( Nouveau )
Mes Événements Show 60 dernières 120 dernières 250 dernières 500 dernières 1000 derniers
Mes Dossiers Ces fichiers sont partagés publiquement, cliquez sur "lien" pour obtenir une URL publique.
✓
✗
Mon Serveur Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Show 60 dernières 120 dernières 250 dernières 500 dernières 1000 derniers
General -
Events - Show 60 dernières 120 dernières 250 dernières 500 dernières 1000 derniers
File Selection
Local file upload Server file selection Agent Remote Desktop Mise à l'échelle
100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Frame rate
Fast Medium Lent Very slow Intel® AMT Hardware KVM Image Encoding
RLE8, le plus rapide RLE16, recommandé RAW8, Slow RAW16, Very Slow {{{title}}}
Mes Appareils Mon Compte Mes Événements Mes Dossiers Mes Utilisateurs Mon Serveur
General Desktop Terminal Files Events Details Intel® AMT Console Plugins
Server disconnected ,click to reconnect .
Mes Appareils Aucun groupe d'appareils.
Mon Compte Device Groups ( Nouveau )
Mes Événements Show 60 dernières 120 dernières 250 dernières 500 dernières 1000 derniers
Mes Dossiers Ces fichiers sont partagés publiquement, cliquez sur "lien" pour obtenir une URL publique.
✓
✗
Mon Serveur Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Show 60 dernières 120 dernières 250 dernières 500 dernières 1000 derniers
General -
Events - Show 60 dernières 120 dernières 250 dernières 500 dernières 1000 derniers
My Server Plugins Nom Description Link Version Latest Statut Action
No plugins on server.
File Selection
Local file upload Server file selection Agent Remote Desktop Mise à l'échelle
100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Frame rate
Fast Medium Lent Very slow Intel® AMT Hardware KVM Image Encoding
RLE8, le plus rapide RLE16, recommandé RAW8, Slow RAW16, Very Slow
diff --git a/webserver.js b/webserver.js
index fb2b311f..0bf4c874 100644
--- a/webserver.js
+++ b/webserver.js
@@ -3214,35 +3214,38 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
});
}
-
- obj.handlePluginAdminReq = function(req, res) {
- const domain = checkUserIpAddress(req, res);
- if (domain == null) { res.sendStatus(404); return; }
- if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
- var user = obj.users[req.session.userid];
- if (user == null) { res.sendStatus(401); return; }
-
- parent.pluginHandler.handleAdminReq(req, res, user, obj);
- }
-
- obj.handlePluginAdminPostReq = function(req, res) {
- const domain = checkUserIpAddress(req, res);
- if (domain == null) { res.sendStatus(404); return; }
- if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
- var user = obj.users[req.session.userid];
- if (user == null) { res.sendStatus(401); return; }
-
- parent.pluginHandler.handleAdminPostReq(req, res, user, obj);
- }
-
- obj.handlePluginJS = function(req, res) {
- const domain = checkUserIpAddress(req, res);
- if (domain == null) { res.sendStatus(404); return; }
- if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
- var user = obj.users[req.session.userid];
- if (user == null) { res.sendStatus(401); return; }
-
- parent.pluginHandler.refreshJS(req, res);
+
+ if (parent.pluginHandler != null) {
+ // Handle a plugin admin request
+ obj.handlePluginAdminReq = function (req, res) {
+ const domain = checkUserIpAddress(req, res);
+ if (domain == null) { res.sendStatus(404); return; }
+ if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
+ var user = obj.users[req.session.userid];
+ if (user == null) { res.sendStatus(401); return; }
+
+ parent.pluginHandler.handleAdminReq(req, res, user, obj);
+ }
+
+ obj.handlePluginAdminPostReq = function (req, res) {
+ const domain = checkUserIpAddress(req, res);
+ if (domain == null) { res.sendStatus(404); return; }
+ if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
+ var user = obj.users[req.session.userid];
+ if (user == null) { res.sendStatus(401); return; }
+
+ parent.pluginHandler.handleAdminPostReq(req, res, user, obj);
+ }
+
+ obj.handlePluginJS = function (req, res) {
+ const domain = checkUserIpAddress(req, res);
+ if (domain == null) { res.sendStatus(404); return; }
+ if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
+ var user = obj.users[req.session.userid];
+ if (user == null) { res.sendStatus(401); return; }
+
+ parent.pluginHandler.refreshJS(req, res);
+ }
}
// Starts the HTTPS server, this should be called after the user/mesh tables are loaded
diff --git a/x.txt b/x.txt
new file mode 100644
index 00000000..535b0541
--- /dev/null
+++ b/x.txt
@@ -0,0 +1,9735 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{{title}}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ My Devices
+ My Account
+ My Events
+ My Files
+ My Users
+ My Server
+ My Plugins
+
+
+
+
+
+
+ General
+ Desktop
+ Terminal
+ Files
+ Events
+ Details
+ Intel® AMT
+ Console
+ Plugins
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Server disconnected , click to reconnect .
+
+
+
My Devices
+
+
+
+
+
+
+
+
+
+ No device groups.
+
+
+
+
+
+
+
+
+
X
+
Location Results
+
+
+
+
+
+
+
+
+
My Account
+
+
+
+
Device Groups
+
( New )
+
+
+
+
+
+
+
My Events
+
+
+
+
+ Show
+
+ Last 60
+ Last 120
+ Last 250
+ Last 500
+ Last 1000
+
+
+
+
+
+
+
+
+
+
+
My Files
+
+
+
+
These files are shared publicly, click "link" to get public url.
+
✓
+
✗
+
+
+
+
+
+
+
My Server
+
+
Server Statistics
+
+
+
+
My Plugins
+
+
+ Name Description Link Version Latest Available Status Action
+
+
Notice:
MeshCentral plugins have been altered. Agent cores require may require an update before full features are available.
+ Click
here to update all Mesh Agent cores.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
+
+
+
+
Remote computer is not powered on, click here to issue a power command.
+
+
+
+
+
+
⇲
+
↺
+
↻
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
+
+
+
+
Remote computer is not powered on, click here to issue a power command.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Show
+
+ Last 60
+ Last 120
+ Last 250
+ Last 500
+ Last 1000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
General -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Events -
+
+
+
+
+
+ Show
+
+ Last 60
+ Last 120
+ Last 250
+ Last 500
+ Last 1000
+
+
+
+
+
+
+
+
+
+
My Server Stats
+
+
+
+ Connections
+ Memory
+
+
+ Last 3 hours
+ Last 8 hours
+ Last day
+ Last week
+ Last 30 days
+
+
+
+
+
+ Log-X
+
+
+
+
+
+
My Server Tracing
+
+
+ Show
+
+ Last 100
+ Last 250
+ Last 500
+ Last 1000
+
+
+
+
+
+
+ None
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
File Selection
+
+ Local file upload
+ Server file selection
+
+
+
+
+
+
+
+
Agent Remote Desktop
+
+
+
Scaling
+
+ 100%
+ 87.5%
+ 75%
+ 62.5%
+ 50%
+ 37.5%
+ 25%
+ 12.5%
+
+
+
+
Frame rate
+
+ Fast
+ Medium
+ Slow
+ Very slow
+
+
+
+
+
Intel® AMT Hardware KVM
+
+
Image Encoding
+
+ RLE8, Fastest
+ RLE16, Recommended
+ RAW8, Slow
+ RAW16, Very Slow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+