mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-23 14:01:43 +03:00
Improved MeshCmd support
This commit is contained in:
parent
da4cade3a4
commit
1aa0e80f53
@ -68,14 +68,17 @@ function run(argv) {
|
|||||||
//console.log('addedModules = ' + JSON.stringify(addedModules));
|
//console.log('addedModules = ' + JSON.stringify(addedModules));
|
||||||
var actionpath = 'meshaction.txt';
|
var actionpath = 'meshaction.txt';
|
||||||
if (args.actionfile != null) { actionpath = args.actionfile; }
|
if (args.actionfile != null) { actionpath = args.actionfile; }
|
||||||
|
var actions = ['ROUTE', 'AMTLMS', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'MEINFO', 'MEVERSIONS', 'MEHASHES'];
|
||||||
|
|
||||||
// Load the action file
|
// Load the action file
|
||||||
var actionfile = null;
|
var actionfile = null;
|
||||||
try { actionfile = fs.readFileSync(actionpath); } catch (e) { }
|
try { actionfile = fs.readFileSync(actionpath); } catch (e) { }
|
||||||
if ((actionpath != 'meshaction.txt') && (actionfile == null)) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].'); exit(1); return; }
|
if ((actionpath != 'meshaction.txt') && (actionfile == null)) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].'); exit(1); return; }
|
||||||
if (actionfile != null) { try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); exit(1); return; } } else { if (argv.length >= 2) { settings = { action: argv[1] } } }
|
if (actionfile != null) { try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); exit(1); return; } } else { if (argv.length >= 2) { settings = { action: argv[1] } } }
|
||||||
|
if (settings == null) { settings = {}; }
|
||||||
|
|
||||||
// Set the arguments
|
// Set the arguments
|
||||||
|
if ((typeof args.action) == 'string') { settings.action = args.action; }
|
||||||
if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); }
|
if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); }
|
||||||
if ((typeof args.remotenodeid) == 'string') { settings.remoteNodeId = args.remotenodeid; }
|
if ((typeof args.remotenodeid) == 'string') { settings.remoteNodeId = args.remotenodeid; }
|
||||||
if ((typeof args.username) == 'string') { settings.username = args.username; }
|
if ((typeof args.username) == 'string') { settings.username = args.username; }
|
||||||
@ -88,9 +91,10 @@ function run(argv) {
|
|||||||
if ((typeof args.serverhttpshash) == 'string') { settings.serverHttpsHash = args.serverhttpshash; }
|
if ((typeof args.serverhttpshash) == 'string') { settings.serverHttpsHash = args.serverhttpshash; }
|
||||||
if ((typeof args.remoteport) == 'string') { settings.remotePort = parseInt(args.remoteport); }
|
if ((typeof args.remoteport) == 'string') { settings.remotePort = parseInt(args.remoteport); }
|
||||||
if ((typeof args.debug) == 'string') { settings.debugLevel = parseInt(args.debug); }
|
if ((typeof args.debug) == 'string') { settings.debugLevel = parseInt(args.debug); }
|
||||||
|
if ((argv.length > 1) && (actions.indexOf(argv[1].toUpperCase()) >= 0)) { settings.action = argv[1]; }
|
||||||
|
|
||||||
// Validate meshaction.txt
|
// Validate meshaction.txt
|
||||||
if ((settings == null) || (settings.action == null)) { console.log('No action specified, valid actions are ROUTE, LOADWEBAPP, CLEARWEBAPP, STORAGESTATE, MEINFO, MEVERSIONS, MEHASHES, LMS.'); exit(1); return; }
|
if (settings.action == null) { console.log('No action specified, valid actions are ' + actions.join(', ') + '.'); exit(1); return; }
|
||||||
settings.action = settings.action.toLowerCase();
|
settings.action = settings.action.toLowerCase();
|
||||||
debug(1, "Settings: " + JSON.stringify(settings));
|
debug(1, "Settings: " + JSON.stringify(settings));
|
||||||
if (settings.action == 'route') { // MeshCentral Router
|
if (settings.action == 'route') { // MeshCentral Router
|
||||||
@ -103,7 +107,7 @@ function run(argv) {
|
|||||||
if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified, use --remoteport [remoteport].'); exit(1); return; }
|
if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified, use --remoteport [remoteport].'); exit(1); return; }
|
||||||
if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); } // Start MeshCentral Router
|
if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); } // Start MeshCentral Router
|
||||||
}
|
}
|
||||||
else if ((settings.action == 'loadwebapp') || (settings.action == 'loadsmallwebapp') || (settings.action == 'loadlargewebapp') || (settings.action == 'clearwebapp') || (settings.action == 'storagestate')) { // Intel AMT Web Application Actions
|
else if ((settings.action == 'amtloadwebapp') || (settings.action == 'amtloadsmallwebapp') || (settings.action == 'amtloadlargewebapp') || (settings.action == 'amtclearwebapp') || (settings.action == 'amtstoragestate')) { // Intel AMT Web Application Actions
|
||||||
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
||||||
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; }
|
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; }
|
||||||
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
|
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
|
||||||
@ -112,13 +116,13 @@ function run(argv) {
|
|||||||
debug(1, "Settings: " + JSON.stringify(settings));
|
debug(1, "Settings: " + JSON.stringify(settings));
|
||||||
digest = require('http-digest').create(settings.username, settings.password);
|
digest = require('http-digest').create(settings.username, settings.password);
|
||||||
digest.http = require('http');
|
digest.http = require('http');
|
||||||
if (settings.action == 'storagestate') {
|
if (settings.action == 'amtstoragestate') {
|
||||||
getAmtStorage(function (statusCode, data) { if (statusCode == 200) { console.log("Storage State: " + JSON.stringify(data, null, 2)); exit(); return; } else { console.log("Unable to read storage state."); exit(); return; } });
|
getAmtStorage(function (statusCode, data) { if (statusCode == 200) { console.log("Storage State: " + JSON.stringify(data, null, 2)); exit(); return; } else { console.log("Unable to read storage state."); exit(); return; } });
|
||||||
} else {
|
} else {
|
||||||
if (settings.action == 'loadwebapp') { settings.webapp = Medium_IntelAmtWebApp; }
|
if (settings.action == 'amtloadwebapp') { settings.webapp = Medium_IntelAmtWebApp; }
|
||||||
else if (settings.action == 'loadsmallwebapp') { settings.webapp = Small_IntelAmtWebApp; }
|
else if (settings.action == 'amtloadsmallwebapp') { settings.webapp = Small_IntelAmtWebApp; }
|
||||||
else if (settings.action == 'loadlargewebapp') { settings.webapp = Large_IntelAmtWebApp; }
|
else if (settings.action == 'amtloadlargewebapp') { settings.webapp = Large_IntelAmtWebApp; }
|
||||||
else if (settings.action == 'clearwebapp') { settings.webapp = null; }
|
else if (settings.action == 'amtclearwebapp') { settings.webapp = null; }
|
||||||
nextStepStorageUpload();
|
nextStepStorageUpload();
|
||||||
}
|
}
|
||||||
} else if ((settings.action == 'meversion') || (settings.action == 'meversions') || (settings.action == 'mever')) {
|
} else if ((settings.action == 'meversion') || (settings.action == 'meversions') || (settings.action == 'mever')) {
|
||||||
@ -141,7 +145,7 @@ function run(argv) {
|
|||||||
exitOnCount = handles.length;
|
exitOnCount = handles.length;
|
||||||
for (var i = 0; i < handles.length; ++i) {
|
for (var i = 0; i < handles.length; ++i) {
|
||||||
this.getCertHashEntry(handles[i], function (result) {
|
this.getCertHashEntry(handles[i], function (result) {
|
||||||
console.log('----------\r\n' + result.name + ', (' + (result.isDefault ? 'Default' : '') + (result.isActive ? ', Active' : ', Disabled') + ')\r\n' + result.hashAlgorithm + ': ' + result.certificateHash);
|
console.log('----------\r\n' + result.name + ', (' + (result.isDefault ? 'Default' : '') + (result.isActive ? ', Active' : ', Disabled') + ')\r\n' + result.hashAlgorithmStr + ': ' + result.certificateHash);
|
||||||
if (--exitOnCount == 0) { console.log('----------'); exit(1); }
|
if (--exitOnCount == 0) { console.log('----------'); exit(1); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -162,15 +166,15 @@ function run(argv) {
|
|||||||
this.getDnsSuffix(function (result) {
|
this.getDnsSuffix(function (result) {
|
||||||
mestate.dns = result;
|
mestate.dns = result;
|
||||||
var str = 'Intel AMT v' + mestate.ver;
|
var str = 'Intel AMT v' + mestate.ver;
|
||||||
if (mestate.ProvisioningState == 'PRE') { str += ', pre-provisioning state'; }
|
if (mestate.ProvisioningState.stateStr == 'PRE') { str += ', pre-provisioning state'; }
|
||||||
else if (mestate.ProvisioningState == 'IN') { str += ', in-provisioning state'; }
|
else if (mestate.ProvisioningState.stateStr == 'IN') { str += ', in-provisioning state'; }
|
||||||
else if (mestate.ProvisioningState == 'POST') { if (mestate.ProvisioningMode == 'ENTERPRISE') { str += ', activated in ' + mestate.controlmode.consoleMode; } else { str += ', activated in ' + mestate.ProvisioningMode; } }
|
else if (mestate.ProvisioningState.stateStr == 'POST') { if (mestate.ProvisioningMode.modeStr == 'ENTERPRISE') { str += ', activated in ' + ["none", "client control mode", "admin control mode", "remote assistance mode"][mestate.controlmode.controlMode]; } else { str += ', activated in ' + mestate.ProvisioningMode.modeStr; } }
|
||||||
if (mestate.ehbc == true) { str += ', EHBC enabled'; }
|
if (mestate.ehbc.EHBC == true) { str += ', EHBC enabled'; }
|
||||||
console.log(str + '.');
|
console.log(str + '.');
|
||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (settings.action == 'lms') {
|
} else if (settings.action == 'amtlms') {
|
||||||
startLms();
|
startLms();
|
||||||
} else {
|
} else {
|
||||||
console.log('Invalid \"action\" specified.'); exit(1); return;
|
console.log('Invalid \"action\" specified.'); exit(1); return;
|
||||||
|
@ -684,7 +684,7 @@ function createMeshCore(agent) {
|
|||||||
var response = null;
|
var response = null;
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case 'help': { // Displays available commands
|
case 'help': { // Displays available commands
|
||||||
response = 'xxxxxxAvailable commands: help, info, args, print, type, dbget, dbset, dbcompact, parseuri, httpget, wslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi.';
|
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseuri, httpget, wslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi.';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'notify': { // Send a notification message to the mesh
|
case 'notify': { // Send a notification message to the mesh
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.1.1-c",
|
"version": "0.1.1-f",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
@ -754,6 +754,7 @@
|
|||||||
hideContextMenu(); // Hide the context menu if present
|
hideContextMenu(); // Hide the context menu if present
|
||||||
QV('verifyEmailId2', false);
|
QV('verifyEmailId2', false);
|
||||||
QV('logoutControl', false);
|
QV('logoutControl', false);
|
||||||
|
serverPoll();
|
||||||
} else if (state == 2) {
|
} else if (state == 2) {
|
||||||
// Fetch list of meshes, nodes, files
|
// Fetch list of meshes, nodes, files
|
||||||
meshserver.Send({ action: 'meshes' });
|
meshserver.Send({ action: 'meshes' });
|
||||||
@ -762,6 +763,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Poll the server, if it responds, refresh the page.
|
||||||
|
function serverPoll() {
|
||||||
|
xdr = null;
|
||||||
|
try { xdr = new XDomainRequest(); } catch (e) { }
|
||||||
|
if (!xdr) xdr = new XMLHttpRequest();
|
||||||
|
xdr.open("HEAD", window.location.href);
|
||||||
|
xdr.timeout = 15000;
|
||||||
|
xdr.onload = function () { reload(); };
|
||||||
|
xdr.onerror = xdr.ontimeout = function () { console.log('error'); setTimeout(serverPoll, 10000); };
|
||||||
|
xdr.send();
|
||||||
|
}
|
||||||
|
|
||||||
// Return true if this browser supports clickonce
|
// Return true if this browser supports clickonce
|
||||||
function detectClickOnce() {
|
function detectClickOnce() {
|
||||||
for (var i in window.navigator.mimeTypes) { if (window.navigator.mimeTypes[i].type == "application/x-ms-application") { return true; } }
|
for (var i in window.navigator.mimeTypes) { if (window.navigator.mimeTypes[i].type == "application/x-ms-application") { return true; } }
|
||||||
@ -1390,7 +1403,10 @@
|
|||||||
r += '</tr></table><div style=height:1px></div>'; // This height of 1 div fixes a problem in Linux firefox browsers
|
r += '</tr></table><div style=height:1px></div>'; // This height of 1 div fixes a problem in Linux firefox browsers
|
||||||
|
|
||||||
// Add a "Add Mesh" option
|
// Add a "Add Mesh" option
|
||||||
if ((view < 3) && (sort == 0) && (meshcount > 0)) { r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:10px title="Create a new group of computers."><a onclick=account_createMesh() style=cursor:pointer>Add Mesh</a></div>'; }
|
r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:10px>';
|
||||||
|
if ((view < 3) && (sort == 0) && (meshcount > 0)) { r += '<a onclick=account_createMesh() title="Create a new group of computers." style=cursor:pointer>Add Mesh</a> '; }
|
||||||
|
r += '<a onclick=p10showMeshCmdDialog(0) style=cursor:pointer title="Download MeshCmd, a command line tool that performs many functions.">MeshCmd</a></div>';
|
||||||
|
r += '</div>';
|
||||||
|
|
||||||
QH('xdevices', r);
|
QH('xdevices', r);
|
||||||
deviceHeaderSet();
|
deviceHeaderSet();
|
||||||
@ -2481,7 +2497,7 @@
|
|||||||
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a> ';
|
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a> ';
|
||||||
if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a> ';
|
if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a> ';
|
||||||
|
|
||||||
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showRouterDialog("' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a> ';
|
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showMeshCmdDialog(1,"' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a> ';
|
||||||
|
|
||||||
// RDP link, show this link only of the remote machine is Windows.
|
// RDP link, show this link only of the remote machine is Windows.
|
||||||
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
|
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
|
||||||
@ -2758,7 +2774,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show router dialog
|
// Show router dialog
|
||||||
function p10showRouterDialog() {
|
function p10showMeshCmdDialog(mode, nodeid) {
|
||||||
if (xxdialogMode) return;
|
if (xxdialogMode) return;
|
||||||
var y = "<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>";
|
var y = "<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>";
|
||||||
y += "<option value=3>Windows (32bit)</option>";
|
y += "<option value=3>Windows (32bit)</option>";
|
||||||
@ -2769,13 +2785,15 @@
|
|||||||
y += "</select>";
|
y += "</select>";
|
||||||
|
|
||||||
var x = "";
|
var x = "";
|
||||||
x += "<div>Download \"meshcmd\" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed.<br /><br />";
|
if (mode == 0) { x += '<div>MeshCmd is a command line tool that performs lots of different operations. The action file can optionally be downloaded and edited to provide server information and credentials.<br /><br />'; }
|
||||||
|
if (mode == 1) { x += '<div>Download "meshcmd" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed.<br /><br />'; }
|
||||||
x += addHtmlValue('Operating System', y);
|
x += addHtmlValue('Operating System', y);
|
||||||
x += addHtmlValue('Mesh Command', '<a id="meshcmddownloadid" href="meshagents?meshcmd=3" target="_blank"></a>');
|
x += addHtmlValue('MeshCmd', '<a id=meshcmddownloadid href="meshagents?meshcmd=3" target="_blank"></a>');
|
||||||
x += addHtmlValue('Action File', '<a href="meshagents?meshaction=route&nodeid=' + currentNode._id + '" target="_blank">MeshAction (.txt)</a>');
|
if (mode == 0) { x += addHtmlValue('Action File', '<a href="meshagents?meshaction=generic" target="_blank">MeshAction (.txt)</a>'); }
|
||||||
|
if (mode == 1) { x += addHtmlValue('Action File', '<a href="meshagents?meshaction=route&nodeid=' + nodeid + '" target="_blank">MeshAction (.txt)</a>'); }
|
||||||
x += "</div>";
|
x += "</div>";
|
||||||
|
|
||||||
setDialogMode(2, "Network Router", 1, null, x, currentNode._id);
|
setDialogMode(2, ["Download MeshCmd","Network Router"][mode], 9, null, x);
|
||||||
meshCmdOsClick();
|
meshCmdOsClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
webserver.js
19
webserver.js
@ -1354,8 +1354,9 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||||||
});
|
});
|
||||||
} else if (req.query.meshaction != null) {
|
} else if (req.query.meshaction != null) {
|
||||||
var domain = checkUserIpAddress(req, res);
|
var domain = checkUserIpAddress(req, res);
|
||||||
|
if (domain == null) { res.sendStatus(404); return; }
|
||||||
var user = obj.users[req.session.userid];
|
var user = obj.users[req.session.userid];
|
||||||
if (domain == null || req.query.nodeid == null) { res.sendStatus(404); return; }
|
if ((req.query.meshaction == 'route') && (req.query.nodeid != null)) {
|
||||||
obj.db.Get(req.query.nodeid, function (err, nodes) {
|
obj.db.Get(req.query.nodeid, function (err, nodes) {
|
||||||
if (nodes.length != 1) { res.sendStatus(401); return; }
|
if (nodes.length != 1) { res.sendStatus(401); return; }
|
||||||
var node = nodes[0];
|
var node = nodes[0];
|
||||||
@ -1377,6 +1378,22 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' });
|
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' });
|
||||||
res.send(JSON.stringify(meshaction, null, ' '));
|
res.send(JSON.stringify(meshaction, null, ' '));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else if (req.query.meshaction == 'generic') {
|
||||||
|
var meshaction = {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
|
||||||
|
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
|
||||||
|
debugLevel: 0
|
||||||
|
}
|
||||||
|
if (user != null) { meshaction.username = user.name; }
|
||||||
|
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.certificates.CommonName + ':' + obj.args.port + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
|
||||||
|
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' });
|
||||||
|
res.send(JSON.stringify(meshaction, null, ' '));
|
||||||
|
} else {
|
||||||
|
res.sendStatus(401);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send a list of available mesh agents
|
// Send a list of available mesh agents
|
||||||
var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
|
var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
|
||||||
|
Loading…
Reference in New Issue
Block a user