Added more commands to MeshCtrl.js

Ylian Saint-Hilaire committed Aug 17, 2020 at 15:01 UTC 7b4e961dffa61c50650d0c1246d08eddb4a19bb0
2 files changed +112 -9
meshctrl.js
+78 -5
@@ -7,7 +7,7 @@ try { require('ws'); } catch (ex) { console.log('Missing module "ws", type "npm
7 var settings = {};
8 const crypto = require('crypto');
9 const args = require('minimist')(process.argv.slice(2));
10 -const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download'];
10 +const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast'];
11 if (args.proxy != null) { try { require('https-proxy-agent'); } catch (ex) { console.log('Missing module "https-proxy-agent", type "npm install https-proxy-agent" to install it.'); return; } }
12
13 if (args['_'].length == 0) {
@@ -46,6 +46,9 @@ if (args['_'].length == 0) {
46 console.log(" Shell - Access command shell of a remote device.");
47 console.log(" Upload - Upload a file to a remote device.");
48 console.log(" Download - Download a file from a remote device.");
49 + console.log(" DeviceOpenUrl - Open a URL on a remote device.");
50 + console.log(" DeviceMessage - Open a message box on a remote device.");
51 + console.log(" DeviceToast - Display a toast notification on a remote device.");
52 console.log("\r\nSupported login arguments:");
53 console.log(" --url [wss://server] - Server url, wss://localhost:443 is default.");
54 console.log(" --loginuser [username] - Login username, admin is default.");
@@ -190,6 +193,24 @@ if (args['_'].length == 0) {
193 else { ok = true; }
194 break;
195 }
196 + case 'deviceopenurl': {
197 + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); }
198 + else if (args.openurl == null) { console.log("Remote URL, use --openurl [url] specify the link to open."); }
199 + else { ok = true; }
200 + break;
201 + }
202 + case 'devicemessage': {
203 + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); }
204 + else if (args.msg == null) { console.log("Remote message, use --msg \"[message]\" specify a remote message."); }
205 + else { ok = true; }
206 + break;
207 + }
208 + case 'devicetoast': {
209 + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); }
210 + else if (args.msg == null) { console.log("Remote message, use --msg \"[message]\" specify a remote message."); }
211 + else { ok = true; }
212 + break;
213 + }
214 case 'help': {
215 if (args['_'].length < 2) {
216 console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
@@ -580,6 +601,48 @@ if (args['_'].length == 0) {
601 console.log(" --target [localpath] - The local path to download the file to.");
602 break;
603 }
604 + case 'deviceopenurl': {
605 + console.log("Open a web page on a remote device, Example usages:\r\n");
606 + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceOpenUrl --id 'deviceid' --url http://meshcentral.com"));
607 + console.log("\r\nRequired arguments:\r\n");
608 + if (process.platform == 'win32') {
609 + console.log(" --id [deviceid] - The device identifier.");
610 + } else {
611 + console.log(" --id '[deviceid]' - The device identifier.");
612 + }
613 + console.log(" --openurl [url] - Link to the web page.");
614 + break;
615 + }
616 + case 'devicemessage': {
617 + console.log("Display a message on the remote device, Example usages:\r\n");
618 + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceMessage --id 'deviceid' --msg \"message\""));
619 + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceMessage --id 'deviceid' --msg \"message\" --title \"title\""));
620 + console.log("\r\nRequired arguments:\r\n");
621 + if (process.platform == 'win32') {
622 + console.log(" --id [deviceid] - The device identifier.");
623 + } else {
624 + console.log(" --id '[deviceid]' - The device identifier.");
625 + }
626 + console.log(" --msg [message] - The message to display.");
627 + console.log("\r\nOptional arguments:\r\n");
628 + console.log(" --title [title] - Messagebox title, default is \"MeshCentral\".");
629 + break;
630 + }
631 + case 'devicetoast': {
632 + console.log("Display a toast message on the remote device, Example usages:\r\n");
633 + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceToast --id 'deviceid' --msg \"message\""));
634 + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceToast --id 'deviceid' --msg \"message\" --title \"title\""));
635 + console.log("\r\nRequired arguments:\r\n");
636 + if (process.platform == 'win32') {
637 + console.log(" --id [deviceid] - The device identifier.");
638 + } else {
639 + console.log(" --id '[deviceid]' - The device identifier.");
640 + }
641 + console.log(" --msg [message] - The message to display.");
642 + console.log("\r\nOptional arguments:\r\n");
643 + console.log(" --title [title] - Toast title, default is \"MeshCentral\".");
644 + break;
645 + }
646 default: {
647 console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
648 }
@@ -973,6 +1036,18 @@ function serverConnect() {
1036 ws.send("{\"action\":\"authcookie\"}");
1037 break;
1038 }
1039 + case 'deviceopenurl': {
1040 + ws.send(JSON.stringify({ action: 'msg', type: 'openUrl', nodeid: args.id, url: args.openurl, responseid: 'meshctrl' }));
1041 + break;
1042 + }
1043 + case 'devicemessage': {
1044 + ws.send(JSON.stringify({ action: 'msg', type: 'messagebox', nodeid: args.id, title: args.title ? args.title : "MeshCentral", msg: args.msg, responseid: 'meshctrl' }));
1045 + break;
1046 + }
1047 + case 'devicetoast': {
1048 + ws.send(JSON.stringify({ action: 'toast', nodeids: [args.id], title: args.title ? args.title : "MeshCentral", msg: args.msg, responseid: 'meshctrl' }));
1049 + break;
1050 + }
1051 }
1052 });
1053
@@ -988,10 +1063,7 @@ function serverConnect() {
1063 var data = null;
1064 try { data = JSON.parse(rawdata); } catch (ex) { }
1065 if (data == null) { console.log('Unable to parse data: ' + rawdata); }
991 - if (settings.cmd == 'showevents') {
992 - console.log(data);
993 - return;
994 - }
1066 + if (settings.cmd == 'showevents') { console.log(data); return; }
1067 switch (data.action) {
1068 case 'serverinfo': { // SERVERINFO
1069 settings.currentDomain = data.serverinfo.domain;
@@ -1054,6 +1126,7 @@ function serverConnect() {
1126 break;
1127 }
1128 case 'msg': // SHELL
1129 + case 'toast': // TOAST
1130 case 'adduser': // ADDUSER
1131 case 'deleteuser': // REMOVEUSER
1132 case 'createmesh': // ADDDEVICEGROUP
meshuser.js
+34 -4
@@ -236,6 +236,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
236 } else { if (func) { func(false); } return false; }
237 }
238 } else { if (func) { func(false); } return false; }
239 + if (func) { func(true); }
240 return true;
241 }
242
@@ -1205,9 +1206,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1206 }
1207 case 'msg':
1208 {
1209 + // Check the nodeid
1210 + if (common.validateString(command.nodeid, 1, 1024) == false) {
1211 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'msg', result: 'Unable to route', tag: command.tag, responseid: command.responseid })); } catch (ex) { } }
1212 + return;
1213 + }
1214 +
1215 // Rights check
1216 var requiredRights = null, requiredNonRights = null;
1217
1218 + // Complete the nodeid if needed
1219 + if (command.nodeid.indexOf('/') == -1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; }
1220 +
1221 // Before routing this command, let's do some security checking.
1222 // If this is a tunnel request, we need to make sure the NodeID in the URL matches the NodeID in the command.
1223 if (command.type == 'tunnel') {
@@ -3577,13 +3587,30 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3587 }
3588 case 'toast':
3589 {
3580 - if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
3581 - if (common.validateString(command.title, 1, 512) == false) break; // Check title
3582 - if (common.validateString(command.msg, 1, 4096) == false) break; // Check message
3590 + var err = null;
3591 +
3592 + // Perform input validation
3593 + try {
3594 + if (common.validateStrArray(command.nodeids, 1, 256) == false) { err = "Invalid nodeids"; } // Check nodeids
3595 + else if (common.validateString(command.title, 1, 512) == false) { err = "Invalid title"; } // Check title
3596 + else if (common.validateString(command.msg, 1, 4096) == false) { err = "Invalid message"; } // Check message
3597 + else {
3598 + var nodeids = [];
3599 + for (i in command.nodeids) { if (command.nodeids[i].indexOf('/') == -1) { nodeids.push('node/' + domain.id + '/' + command.nodeids[i]); } else { nodeids.push(command.nodeids[i]); } }
3600 + command.nodeids = nodeids;
3601 + }
3602 + } catch (ex) { console.log(ex); err = "Validation exception: " + ex; }
3603 +
3604 + // Handle any errors
3605 + if (err != null) {
3606 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'toast', responseid: command.responseid, result: err })); } catch (ex) { } }
3607 + break;
3608 + }
3609 +
3610 for (i in command.nodeids) {
3611 // Get the node and the rights for this node
3612 parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
3586 - // Check we have the rights to delete this device
3613 + // Check we have the rights to notify this device
3614 if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return;
3615
3616 // Get this device and send toast command
@@ -3593,6 +3620,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3620 }
3621 });
3622 }
3623 +
3624 + // Send response if required
3625 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'toast', responseid: command.responseid, result: 'ok' })); } catch (ex) { } }
3626 break;
3627 }
3628 case 'getnetworkinfo':