From 8dcef34205872759e615f01cfd14bda4292d0fb9 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 17 Aug 2020 15:01:25 -0700 Subject: [PATCH] Added more commands to MeshCtrl.js --- meshctrl.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++++---- meshuser.js | 38 +++++++++++++++++++++--- 2 files changed, 112 insertions(+), 9 deletions(-) diff --git a/meshctrl.js b/meshctrl.js index 98cc6571..cbb3adbe 100644 --- a/meshctrl.js +++ b/meshctrl.js @@ -7,7 +7,7 @@ try { require('ws'); } catch (ex) { console.log('Missing module "ws", type "npm var settings = {}; const crypto = require('crypto'); const args = require('minimist')(process.argv.slice(2)); -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']; +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']; 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; } } if (args['_'].length == 0) { @@ -46,6 +46,9 @@ if (args['_'].length == 0) { console.log(" Shell - Access command shell of a remote device."); console.log(" Upload - Upload a file to a remote device."); console.log(" Download - Download a file from a remote device."); + console.log(" DeviceOpenUrl - Open a URL on a remote device."); + console.log(" DeviceMessage - Open a message box on a remote device."); + console.log(" DeviceToast - Display a toast notification on a remote device."); console.log("\r\nSupported login arguments:"); console.log(" --url [wss://server] - Server url, wss://localhost:443 is default."); console.log(" --loginuser [username] - Login username, admin is default."); @@ -190,6 +193,24 @@ if (args['_'].length == 0) { else { ok = true; } break; } + case 'deviceopenurl': { + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); } + else if (args.openurl == null) { console.log("Remote URL, use --openurl [url] specify the link to open."); } + else { ok = true; } + break; + } + case 'devicemessage': { + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); } + else if (args.msg == null) { console.log("Remote message, use --msg \"[message]\" specify a remote message."); } + else { ok = true; } + break; + } + case 'devicetoast': { + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); } + else if (args.msg == null) { console.log("Remote message, use --msg \"[message]\" specify a remote message."); } + else { ok = true; } + break; + } case 'help': { if (args['_'].length < 2) { 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) { console.log(" --target [localpath] - The local path to download the file to."); break; } + case 'deviceopenurl': { + console.log("Open a web page on a remote device, Example usages:\r\n"); + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceOpenUrl --id 'deviceid' --url http://meshcentral.com")); + console.log("\r\nRequired arguments:\r\n"); + if (process.platform == 'win32') { + console.log(" --id [deviceid] - The device identifier."); + } else { + console.log(" --id '[deviceid]' - The device identifier."); + } + console.log(" --openurl [url] - Link to the web page."); + break; + } + case 'devicemessage': { + console.log("Display a message on the remote device, Example usages:\r\n"); + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceMessage --id 'deviceid' --msg \"message\"")); + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceMessage --id 'deviceid' --msg \"message\" --title \"title\"")); + console.log("\r\nRequired arguments:\r\n"); + if (process.platform == 'win32') { + console.log(" --id [deviceid] - The device identifier."); + } else { + console.log(" --id '[deviceid]' - The device identifier."); + } + console.log(" --msg [message] - The message to display."); + console.log("\r\nOptional arguments:\r\n"); + console.log(" --title [title] - Messagebox title, default is \"MeshCentral\"."); + break; + } + case 'devicetoast': { + console.log("Display a toast message on the remote device, Example usages:\r\n"); + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceToast --id 'deviceid' --msg \"message\"")); + console.log(winRemoveSingleQuotes(" MeshCtrl DeviceToast --id 'deviceid' --msg \"message\" --title \"title\"")); + console.log("\r\nRequired arguments:\r\n"); + if (process.platform == 'win32') { + console.log(" --id [deviceid] - The device identifier."); + } else { + console.log(" --id '[deviceid]' - The device identifier."); + } + console.log(" --msg [message] - The message to display."); + console.log("\r\nOptional arguments:\r\n"); + console.log(" --title [title] - Toast title, default is \"MeshCentral\"."); + break; + } default: { console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.'); } @@ -973,6 +1036,18 @@ function serverConnect() { ws.send("{\"action\":\"authcookie\"}"); break; } + case 'deviceopenurl': { + ws.send(JSON.stringify({ action: 'msg', type: 'openUrl', nodeid: args.id, url: args.openurl, responseid: 'meshctrl' })); + break; + } + case 'devicemessage': { + ws.send(JSON.stringify({ action: 'msg', type: 'messagebox', nodeid: args.id, title: args.title ? args.title : "MeshCentral", msg: args.msg, responseid: 'meshctrl' })); + break; + } + case 'devicetoast': { + ws.send(JSON.stringify({ action: 'toast', nodeids: [args.id], title: args.title ? args.title : "MeshCentral", msg: args.msg, responseid: 'meshctrl' })); + break; + } } }); @@ -988,10 +1063,7 @@ function serverConnect() { var data = null; try { data = JSON.parse(rawdata); } catch (ex) { } if (data == null) { console.log('Unable to parse data: ' + rawdata); } - if (settings.cmd == 'showevents') { - console.log(data); - return; - } + if (settings.cmd == 'showevents') { console.log(data); return; } switch (data.action) { case 'serverinfo': { // SERVERINFO settings.currentDomain = data.serverinfo.domain; @@ -1054,6 +1126,7 @@ function serverConnect() { break; } case 'msg': // SHELL + case 'toast': // TOAST case 'adduser': // ADDUSER case 'deleteuser': // REMOVEUSER case 'createmesh': // ADDDEVICEGROUP diff --git a/meshuser.js b/meshuser.js index 5e60c1e4..6e409491 100644 --- a/meshuser.js +++ b/meshuser.js @@ -236,6 +236,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } else { if (func) { func(false); } return false; } } } else { if (func) { func(false); } return false; } + if (func) { func(true); } return true; } @@ -1205,9 +1206,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'msg': { + // Check the nodeid + if (common.validateString(command.nodeid, 1, 1024) == false) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'msg', result: 'Unable to route', tag: command.tag, responseid: command.responseid })); } catch (ex) { } } + return; + } + // Rights check var requiredRights = null, requiredNonRights = null; + // Complete the nodeid if needed + if (command.nodeid.indexOf('/') == -1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; } + // Before routing this command, let's do some security checking. // If this is a tunnel request, we need to make sure the NodeID in the URL matches the NodeID in the command. if (command.type == 'tunnel') { @@ -3577,13 +3587,30 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'toast': { - if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's - if (common.validateString(command.title, 1, 512) == false) break; // Check title - if (common.validateString(command.msg, 1, 4096) == false) break; // Check message + var err = null; + + // Perform input validation + try { + if (common.validateStrArray(command.nodeids, 1, 256) == false) { err = "Invalid nodeids"; } // Check nodeids + else if (common.validateString(command.title, 1, 512) == false) { err = "Invalid title"; } // Check title + else if (common.validateString(command.msg, 1, 4096) == false) { err = "Invalid message"; } // Check message + else { + var nodeids = []; + 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]); } } + command.nodeids = nodeids; + } + } catch (ex) { console.log(ex); err = "Validation exception: " + ex; } + + // Handle any errors + if (err != null) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'toast', responseid: command.responseid, result: err })); } catch (ex) { } } + break; + } + for (i in command.nodeids) { // Get the node and the rights for this node parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { - // Check we have the rights to delete this device + // Check we have the rights to notify this device if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return; // Get this device and send toast command @@ -3593,6 +3620,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } }); } + + // Send response if required + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'toast', responseid: command.responseid, result: 'ok' })); } catch (ex) { } } break; } case 'getnetworkinfo':