Added Intel AMT watchdog presence in MeshCmd

Ylian Saint-Hilaire committed Apr 28, 2018 at 15:36 UTC 024023247d1d30c491fd40facf86395285178626
9 files changed +168 -25
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/meshcmd.js
+97 -4
@@ -91,7 +91,7 @@ function run(argv) {
91 //console.log('addedModules = ' + JSON.stringify(addedModules));
92 var actionpath = 'meshaction.txt';
93 if (args.actionfile != null) { actionpath = args.actionfile; }
94 - var actions = ['HELP', 'ROUTE', 'MICROLMS', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'AMTINFO', 'AMTVERSIONS', 'AMTHASHES', 'AMTSAVESTATE', 'AMTSCRIPT', 'AMTUUID', 'AMTCCM', 'AMTDEACTIVATE', 'SMBIOS', 'RAWSMBIOS', 'MESHCOMMANDER', 'AMTAUDITLOG'];
94 + var actions = ['HELP', 'ROUTE', 'MICROLMS', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'AMTINFO', 'AMTVERSIONS', 'AMTHASHES', 'AMTSAVESTATE', 'AMTSCRIPT', 'AMTUUID', 'AMTCCM', 'AMTDEACTIVATE', 'SMBIOS', 'RAWSMBIOS', 'MESHCOMMANDER', 'AMTAUDITLOG', 'AMTPRESENCE'];
95
96 // Load the action file
97 var actionfile = null;
@@ -117,6 +117,7 @@ function run(argv) {
117 if ((typeof args.output) == 'string') { settings.output = args.output; }
118 if ((typeof args.debug) == 'string') { settings.debugLevel = parseInt(args.debug); }
119 if ((typeof args.script) == 'string') { settings.script = args.script; }
120 + if ((typeof args.agent) == 'string') { settings.agent = args.agent; }
121 if (args.noconsole) { settings.noconsole = true; }
122 if (args.nocommander) { settings.noconsole = true; }
123 if (args.lmsdebug) { settings.lmsdebug = true; }
@@ -147,6 +148,7 @@ function run(argv) {
148 console.log(' AmtClearWebApp - Clear everything from Intel AMT web storage.');
149 console.log(' AmtStorageState - Show contents of the Intel AMT web storage.');
150 console.log(' AmtSaveState - Save all Intel AMT WSMAN object to file.');
151 + console.log(' AmtPresence - Heartbeat a local Intel AMT watchdog agent.');
152 console.log(' AmtScript - Run .mescript on Intel AMT.');
153 console.log('\r\nHelp on a specific action using:\r\n');
154 console.log(' meshcmd help [action]');
@@ -221,6 +223,12 @@ function run(argv) {
223 console.log(' --user [username] The Intel AMT login username, admin is default.');
224 console.log(' --pass [password] The Intel AMT login password.');
225 console.log(' --tls Specifies that TLS must be used.');
226 + } else if (action == 'amtpresence') {
227 + console.log('AmtPresence will heartbeat a local Intel AMT watchdog agent. Example usage:\r\n\r\n meshcmd amtpresence --agent B4B6A24C-255E-A75C-F5E8-B00B4D946AA7');
228 + console.log('\r\nPossible arguments:\r\n');
229 + console.log(' --user [username] The Intel AMT login username, admin is default.');
230 + console.log(' --pass [password] The Intel AMT login password.');
231 + console.log(' --agent [uuid] The unique identifier of the watchdog agent.');
232 } else if (action == 'amtscript') {
233 console.log('AmtScript will run a .mescript file on the local or remote Intel AMT. Script files can be built using the MeshCommander script editor and be used to setup or perform actions on Intel AMT. Example usage:\r\n\r\n meshcmd amtscript --script myscript.mescript --host 1.2.3.4 --user admin --pass mypassword --tls');
234 console.log('\r\nPossible arguments:\r\n');
@@ -373,6 +381,12 @@ function run(argv) {
381 startLms(function (state) {
382 console.log(['MicroLMS did not start. Must run as administrator or LMS already active.', 'MicroLMS started.', 'MicroLMS started, MeshCommander on HTTP/16994.', 'MEI error'][state]); console.log('Press ctrl-c to exit.'); if (state == 0) { exit(0); }
383 });
384 + } else if (settings.action == 'amtpresence') {
385 + // Heartbeat a Intel AMT watchdog
386 + if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
387 + if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
388 + if ((settings.agent == null) || (typeof settings.agent != 'string') || (settings.agent == '')) { console.log('No or invalid \"agent\" specified, use --agent [agent].'); exit(1); return; }
389 + performAmtAgentPresence();
390 } else if (settings.action == 'amtscript') {
391 // Start running a MEScript
392 if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
@@ -414,6 +428,84 @@ function run(argv) {
428 }
429 }
430
431 +//
432 +// Intel AMT Agent Presence
433 +//
434 +
435 +function performAmtAgentPresence() { startLms(function () { tempWatchdogTimer = setTimeout(performAmtAgentPresenceRegister, 3000); }); }
436 +
437 +function performAmtAgentPresenceRegister() {
438 + // Setup the Intel AMT WSMAN stack
439 + tempWatchdogTimer = null;
440 + var transport = require('amt-wsman-duk');
441 + var wsman = require('amt-wsman');
442 + var amt = require('amt');
443 + wsstack = new wsman(transport, '127.0.0.1', settings.tls ? 16993 : 16992, settings.username, settings.password, settings.tls);
444 + amtstack = new amt(wsstack);
445 +
446 + // Register the watchdog
447 + watchdog = { DeviceID: Buffer.from(guidToStr(settings.agent.split('-').join('')).split('-').join(''), 'hex').toString('base64'), Retry: 0 };
448 + amtstack.AMT_AgentPresenceWatchdog_RegisterAgent(performAmtAgentPresenceRegisterRetry, watchdog, watchdog.Seq, { 'DeviceID': watchdog.DeviceID });
449 +}
450 +
451 +// Called after the agent is registered
452 +function performAmtAgentPresenceRegisterRetry(stack, name, response, status, watchdog) {
453 + if ((status == 200) && (response.Body.SessionSequenceNumber) && (response.Body.TimeoutInterval)) {
454 + console.log('Asserting presence of the watchdog...');
455 + watchdog.Seq = response.Body.SessionSequenceNumber;
456 + watchdog.Interval = response.Body.TimeoutInterval * 800;
457 + watchdog.Retry = 0;
458 + tempWatchdogTimer = setTimeout(performAmtAgentPresenceAssert, watchdog.Interval);
459 + } else {
460 + debug(1, 'Failed to register, status = ' + status);
461 + watchdog.Retry++;
462 + if (watchdog.Retry < 5) {
463 + tempWatchdogTimer = setTimeout(function () { amtstack.AMT_AgentPresenceWatchdog_RegisterAgent(performAmtAgentPresenceRegisterRetry, watchdog, watchdog.Seq, { 'DeviceID': watchdog.DeviceID }); }, 1000);
464 + } else {
465 + console.log('Failed to register this watchdog.');
466 + process.exit(0);
467 + }
468 + }
469 +}
470 +
471 +// Start a new agent assert
472 +function performAmtAgentPresenceAssert() {
473 + watchdog.Seq++;
474 + amtstack.AMT_AgentPresenceWatchdog_AssertPresence(watchdog.Seq, performAmtAgentPresenceAssertRetry, watchdog, 0, { 'DeviceID': watchdog.DeviceID });
475 +}
476 +
477 +// Called after the agent is asserted
478 +function performAmtAgentPresenceAssertRetry(stack, name, response, status, watchdog) {
479 + if (status == 200) {
480 + debug(1, 'Succesful assert, sequence = ' + watchdog.Seq);
481 + watchdog.Retry = 0;
482 + tempWatchdogTimer = setTimeout(performAmtAgentPresenceAssert, watchdog.Interval);
483 + } else {
484 + debug(1, 'Failed to assert, status = ' + status);
485 + watchdog.Retry++;
486 + if (watchdog.Retry < 5) {
487 + amtstack.AMT_AgentPresenceWatchdog_AssertPresence(watchdog.Seq, performAmtAgentPresenceAssertRetry, watchdog, 0, { 'DeviceID': watchdog.DeviceID });
488 + } else {
489 + console.log('Failed to assert presence on this watchdog.');
490 + process.exit(0);
491 + }
492 + }
493 +}
494 +
495 +function performAmtAgentPresenceEx5(stack, name, response, status, watchdog) {
496 + console.log('b', status, watchdog);
497 + if (status == 200) {
498 + watchdog.Retry = 0;
499 + } else {
500 + watchdog.Retry++;
501 + if (watchdog.Retry < 5) {
502 + amtstack.AMT_AgentPresenceWatchdog_AssertPresence(watchdog.Seq, performAmtAgentPresenceEx4, watchdog, 0, { 'DeviceID': watchdog.DeviceID });
503 + } else {
504 + console.log('Failed to assert presence on this watchdog.');
505 + process.exit(0);
506 + }
507 + }
508 +}
509
510 //
511 // Intel AMT Audit Log
@@ -868,9 +960,10 @@ function setupMeiOsAdmin(func, state) {
960 //var AllWsman = "CIM_SoftwareIdentity,IPS_SecIOService,IPS_ScreenSettingData,IPS_ProvisioningRecordLog,IPS_HostBasedSetupService,IPS_HostIPSettings,IPS_IPv6PortSettings".split(',');
961 //osamtstack.BatchEnum(null, AllWsman, startLmsWsmanResponse, null, true);
962
871 - tempTimer = setInterval(function () { kvmGetData(true); }, 2000);
872 - kvmGetData(false);
873 - kvmSetData(JSON.stringify({ action: 'restart', ver: 1 }));
963 + //*************************************
964 + //tempTimer = setInterval(function () { kvmGetData(true); }, 2000);
965 + //kvmGetData(false);
966 + //kvmSetData(JSON.stringify({ action: 'restart', ver: 1 }));
967 });
968 }
969
agents/modules_meshcmd/amt.js
+6 -3
@@ -199,9 +199,12 @@ function AmtStackCreateService(wsmanStack) {
199 }
200
201 // Auto generated methods
202 - obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func); }
203 - obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
204 - obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
202 + obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func, tag, pri, selectors); }
203 + obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func, tag, pri, selectors); }
204 + obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func, tag, pri, selectors); }
205 + //obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func); }
206 + //obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
207 + //obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
208 obj.AMT_AgentPresenceWatchdog_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func, tag, pri, selectors); }
209 obj.AMT_AgentPresenceWatchdog_DeleteAllActions = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "DeleteAllActions", {}, callback_func, tag, pri, selectors); }
210 obj.AMT_AgentPresenceWatchdogAction_GetActionEac = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogAction", "GetActionEac", {}, callback_func); }
mpsserver.js
+1
@@ -216,6 +216,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
216 passwordLen = common.ReadInt(data, 14 + usernameLen + serviceNameLen + methodNameLen);
217 password = data.substring(18 + usernameLen + serviceNameLen + methodNameLen, 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen);
218 }
219 + //console.log('MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
220 Debug(3, 'MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
221
222 // Check the CIRA password
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.6-y",
3 + "version": "0.1.7-a",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/scripts/agent-desktop-0.0.2.js
+3 -1
@@ -272,7 +272,9 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
272 }
273
274 obj.SendKeyMsgKC = function (action, kc) {
275 - if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, (action - 1), kc));
275 + if (obj.State != 3) return;
276 + if (typeof action == 'object') { for (var i in action) { obj.SendKeyMsgKC(action[i][0], action[i][1]); } }
277 + else { obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, (action - 1), kc)); }
278 }
279
280 obj.sendcad = function() { obj.SendCtrlAltDelMsg(); }
public/scripts/amt-desktop-0.0.2.js
+5 -9
@@ -550,17 +550,13 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
550 return obj.haltEvent(e);
551 }
552
553 - obj.sendkey = function (k, d) { obj.send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); }
553 + obj.sendkey = function (k, d) {
554 + if (typeof k == 'object') { for (var i in k) { obj.sendkey(k[i][0], k[i][1]); } }
555 + else { obj.send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); }
556 + }
557
558 obj.SendCtrlAltDelMsg = function () { obj.sendcad(); }
556 - obj.sendcad = function () {
557 - obj.sendkey(0xFFE3, 1); // Control
558 - obj.sendkey(0xFFE9, 1); // Alt
559 - obj.sendkey(0xFFFF, 1); // Delete
560 - obj.sendkey(0xFFFF, 0); // Delete
561 - obj.sendkey(0xFFE9, 0); // Alt
562 - obj.sendkey(0xFFE3, 0); // Control
563 - }
559 + obj.sendcad = function () { obj.sendkey([[0xFFE3, 1], [0xFFE9, 1], [0xFFFF, 1], [0xFFFF, 0], [0xFFE9, 0], [0xFFE3, 0]]); } // Control down, Alt down, Delete down, Delete up , Alt up , Control up
560
561 var _MouseInputGrab = false;
562 var _KeyInputGrab = false;
views/default.handlebars
+55 -7
@@ -389,9 +389,16 @@
389 <input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()">&nbsp;
390 </div>
391 <div>
392 - &nbsp;
393 - <input id="DeskCAD" type="button" value="Ctrl-Alt-Del" onkeypress="return false" onkeydown="return false" onclick="sendCAD()">&nbsp;
394 - <span title="Toggle mouse and keyboard input"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false" onclick="toggleKvmControl()">Input</span>&nbsp;
392 + <select style="margin-left:6px" id="deskkeys">
393 + <option value=0>Win+Down</option>
394 + <option value=1>Win+Up</option>
395 + <option value=2>Win+L</option>
396 + <option value=3>Win+M</option>
397 + <option value=4>Shift+Win+M</option>
398 + </select>
399 + <input id="DeskWD" type=button value="Send" onkeypress="return false" onkeydown="return false" onclick="deskSendKeys()">
400 + <input id="DeskCAD" style="margin-left:6px" type="button" value="Ctrl-Alt-Del" onkeypress="return false" onkeydown="return false" onclick="sendCAD()">
401 + <span style="margin-left:6px" title="Toggle mouse and keyboard input"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false" onclick="toggleKvmControl()">Input</span>&nbsp;
402 </div>
403 </td>
404 </tr>
@@ -1094,7 +1101,8 @@
1101 if (message.current == message.latest) {
1102 setDialogMode(2, "MeshCentral Version", 1, null, x);
1103 } else {
1097 - setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x + '<br />Select OK to start server self-update.');
1104 + setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x + '<br /><input id=d2updateCheck type=checkbox onclick=server_showVersionDlgUpdate() /> Check and click OK to start server self-update.');
1105 + server_showVersionDlgUpdate();
1106 }
1107 }
1108 break;
@@ -3318,6 +3326,10 @@
3326 QE('deskSaveBtn', deskState == 3);
3327 QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
3328 QE('DeskCAD', deskState == 3);
3329 + QE('DeskWD', deskState == 3);
3330 + QE('deskkeys', deskState == 3);
3331 + QV('DeskWD', (currentNode.agent) && (currentNode.agent.id < 5));
3332 + QV('deskkeys', (currentNode.agent) && (currentNode.agent.id < 5));
3333 QE('DeskToolsButton', online);
3334 QV('DeskToastButton', (currentNode.agent) && (currentNode.agent.id < 5));
3335 QE('DeskToastButton', online);
@@ -3493,6 +3505,43 @@
3505 }
3506 }
3507
3508 + // Remote desktop special key combos for Windows
3509 + function deskSendKeys() {
3510 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
3511 + var ks = Q('deskkeys').value;
3512 + if (ks == 0) { // WIN+Down arrow
3513 + if (desktop.contype == 2) {
3514 + desktop.m.sendkey([[0xffe7,1],[0xff54,1],[0xff54,0],[0xffe7,0]]); // Intel AMT: Meta-left down, Down arrow press, Down arrow release, Meta-left release
3515 + } else {
3516 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,40],[desktop.m.KeyAction.UP,40],[desktop.m.KeyAction.EXUP,0x5B]]); // Agent: L-Winkey press, Down arrow press, Down arrow release, L-Winkey release
3517 + }
3518 + } else if (ks == 1) { // WIN+Up arrow
3519 + if (desktop.contype == 2) {
3520 + desktop.m.sendkey([[0xffe7,1],[0xff52,1],[0xff52,0],[0xffe7,0]]); // Intel AMT: Meta-left down, Up arrow press, Up arrow release, Meta-left release
3521 + } else {
3522 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,38],[desktop.m.KeyAction.UP,38],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, Up arrow press, Up arrow release, L-Winkey release
3523 + }
3524 + } else if (ks == 2) { // WIN+L arrow
3525 + if (desktop.contype == 2) {
3526 + desktop.m.sendkey([[0xffe7,1],[0x6c,1],[0x6c,0],[0xffe7,0]]); // Intel AMT: Meta-left down, 'l' press, 'l' release, Meta-left release
3527 + } else {
3528 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,76],[desktop.m.KeyAction.UP,76],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, 'L' press, 'L' release, L-Winkey release
3529 + }
3530 + } else if (ks == 3) { // WIN+M arrow
3531 + if (desktop.contype == 2) {
3532 + desktop.m.sendkey([[0xffe7,1],[0x6d,1],[0x6d,0],[0xffe7,0]]); // Intel AMT: Meta-left down, 'm' press, 'm' release, Meta-left release
3533 + } else {
3534 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,77],[desktop.m.KeyAction.UP,77],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, 'M' press, 'M' release, L-Winkey release
3535 + }
3536 + } else if (ks == 4) { // Shift+WIN+M arrow
3537 + if (desktop.contype == 2) {
3538 + desktop.m.sendkey([[0xffe1,1],[0xffe7,1],[0x6d,1],[0x6d,0],[0xffe7,0],[0xffe1,0]]); // Intel AMT: Shift-left down, Meta-left down, 'm' press, 'm' release, Meta-left release, Shift-left release
3539 + } else {
3540 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.DOWN,16],[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,77],[desktop.m.KeyAction.UP,77],[desktop.m.KeyAction.EXUP,0x5B],[desktop.m.KeyAction.UP, 16]]); // MeshAgent: L-shift press, L-Winkey press, 'M' press, 'M' release, L-Winkey release, L-shift release
3541 + }
3542 + }
3543 + }
3544 +
3545 // Send CTRL-ALT-DEL
3546 function sendCAD() {
3547 if (xxdialogMode || desktop == null || desktop.State != 3) return;
@@ -4538,9 +4587,8 @@
4587 meshserver.send({ action: 'serverversion' });
4588 }
4589
4541 - function server_showVersionDlgEx() {
4542 - meshserver.send({ action: 'serverupdate' });
4543 - }
4590 + function server_showVersionDlgUpdate() { QE('idx_dlgOkButton', Q('d2updateCheck').checked); }
4591 + function server_showVersionDlgEx() { meshserver.send({ action: 'serverupdate' }); }
4592
4593 //
4594 // MY MESHS