diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe
index fb2963c1..ca82f87a 100644
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe
index 32277556..84e55186 100644
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
diff --git a/agents/MeshService-signed.exe b/agents/MeshService-signed.exe
index 1bc0bbf1..3ed2476e 100644
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
diff --git a/agents/MeshService.exe b/agents/MeshService.exe
index 6ede85d0..dd6028df 100644
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
diff --git a/agents/MeshService64-signed.exe b/agents/MeshService64-signed.exe
index 8a4c71bc..3700801e 100644
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe
index 2cd18319..cab74744 100644
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
diff --git a/agents/meshcore.js b/agents/meshcore.js
index 04701b77..7dd688db 100644
--- a/agents/meshcore.js
+++ b/agents/meshcore.js
@@ -436,14 +436,23 @@ function createMeshCore(agent) {
break;
}
case 'ps': {
+ // Return the list of running processes
if (data.sessionid) {
processManager.getProcesses(function (plist) { mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid }); });
}
break;
}
case 'pskill': {
- //sendConsoleText(JSON.stringify(data));
- try { process.kill(data.value); } catch (e) { sendConsoleText(JSON.stringify(e)); }
+ // Kill a process
+ if (data.value) {
+ try { process.kill(data.value); } catch (e) { sendConsoleText(JSON.stringify(e)); }
+ }
+ break;
+ }
+ case 'openUrl': {
+ // Open a local web browser and return success/fail
+ sendConsoleText('OpenURL: ' + data.url);
+ if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
break;
}
}
@@ -477,6 +486,12 @@ function createMeshCore(agent) {
if (data.title && data.msg) { require('toaster').Toast(data.title, data.msg); }
break;
}
+ case 'openUrl': {
+ // Open a local web browser and return success/fail
+ sendConsoleText('OpenURL: ' + data.url);
+ if (data.url) { mesh.SendCommand({ "action": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
+ break;
+ }
}
}
}
@@ -964,13 +979,32 @@ function createMeshCore(agent) {
response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
};
+ // Open a web browser to a specified URL on current user's desktop
+ function openUserDesktopUrl(url) {
+ var child = null;
+ try {
+ switch (process.platform) {
+ case 'win32':
+ child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ["/c", "start", url], { type: childProcess.SpawnTypes.USER });
+ break;
+ case 'linux':
+ child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { type: require('child_process').SpawnTypes.DETACHED, uid: require('user-sessions').consoleUid() });
+ break;
+ case 'darwin':
+ child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
+ break;
+ }
+ } catch (ex) { }
+ return child;
+ }
+
// Process a mesh agent console command
function processConsoleCommand(cmd, args, rights, sessionid) {
try {
var response = null;
switch (cmd) {
case 'help': { // Displays available commands
- response = 'Available commands: help, info, osinfo, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios, toast, lock, users, sendcaps.';
+ response = 'Available commands: help, info, osinfo,args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios, toast, lock, users, sendcaps, openurl.';
break;
}
/*
@@ -992,6 +1026,11 @@ function createMeshCore(agent) {
}
break;
*/
+ case 'openurl': {
+ if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
+ else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
+ break;
+ }
case 'users': {
if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
break;
@@ -1520,7 +1559,7 @@ function createMeshCore(agent) {
//amtLms.on('bind', function (map) { });
amtLms.on('notify', function (data, options, str, code) {
if (code == 'iAMT0052-3') {
- kvmGetData();
+ obj.kvmGetData();
} else {
//if (str != null) { sendConsoleText('Intel AMT LMS: ' + str); }
handleAmtNotification(data);
@@ -1597,7 +1636,7 @@ function createMeshCore(agent) {
});
}
- obj.kvmGetData = function(tag) {
+ obj.kvmGetData = function (tag) {
obj.osamtstack.IPS_KVMRedirectionSettingData_DataChannelRead(obj.kvmDataGetResponse, tag);
}
diff --git a/agents/modules_meshcore/amt-wsman-duk.js b/agents/modules_meshcore/amt-wsman-duk.js
index ccc2a4e0..f3aefd93 100644
--- a/agents/modules_meshcore/amt-wsman-duk.js
+++ b/agents/modules_meshcore/amt-wsman-duk.js
@@ -88,8 +88,10 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
obj.digest.http = require('http');
}
var request = { protocol: (obj.tls == 1 ? 'https:' : 'http:'), method: 'POST', host: obj.host, path: '/wsman', port: obj.port, rejectUnauthorized: false, checkServerIdentity: function (cert) { console.log('checkServerIdentity', JSON.stringify(cert)); } };
+
var req = obj.digest.request(request);
//console.log('Request ' + (obj.RequestCount++));
+
req.on('error', function (e) { obj.gotNextMessagesError({ status: 600 }, 'error', null, [postdata, callback, tag]); });
req.on('response', function (response) {
//console.log('Response: ' + response.statusCode);
@@ -105,6 +107,7 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
// Send POST body, this work with binary.
req.end(postdata);
+
obj.ActiveAjaxCount++;
return req;
}
diff --git a/meshrelay.js b/meshrelay.js
index 16f21a81..1cf73853 100644
--- a/meshrelay.js
+++ b/meshrelay.js
@@ -98,7 +98,14 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
if (obj.id.startsWith('meshmessenger/')) {
if (obj.user == null) { try { obj.close(); } catch (e) { } return null; }
var x = obj.id.split('/'), user1 = x[1] + '/' + x[2] + '/' + x[3], user2 = x[4] + '/' + x[5] + '/' + x[6];
- if ((obj.user._id != user1) && (obj.user._id != user2)) { try { obj.close(); } catch (e) { } return null; }
+ if ((x[1] != 'user') && (x[4] != 'user')) { try { obj.close(); } catch (e) { } return null; } // MeshMessenger session must have at least one authenticated user
+ if ((x[1] == 'user') && (x[4] == 'user')) {
+ // If this is a user-to-user session, you must be authenticated to join.
+ if ((obj.user._id != user1) && (obj.user._id != user2)) { try { obj.close(); } catch (e) { } return null; }
+ } else {
+ // If only one side of the session is a user
+ // !!!!! TODO: Need to make sure that one of the two sides is the correct user. !!!!!
+ }
}
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
diff --git a/meshuser.js b/meshuser.js
index c5a7e3f4..3caa6079 100644
--- a/meshuser.js
+++ b/meshuser.js
@@ -76,6 +76,40 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
ds.on('close', function () { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); });
}
+ // Route a command to a target node
+ function routeCommandToNode(command) {
+ if (obj.common.validateString(command.nodeid, 8, 128) == false) return false;
+ var splitnodeid = command.nodeid.split('/');
+ // Check that we are in the same domain and the user has rights over this node.
+ if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domain.id)) {
+ // See if the node is connected
+ var agent = obj.parent.wsagents[command.nodeid];
+ if (agent != null) {
+ // Check if we have permission to send a message to that node
+ var rights = user.links[agent.dbMeshKey];
+ if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission, 256 is desktop read only
+ command.sessionid = ws.sessionId; // Set the session id, required for responses.
+ command.rights = rights.rights; // Add user rights flags to the message
+ delete command.nodeid; // Remove the nodeid since it's implyed.
+ try { agent.send(JSON.stringify(command)); } catch (ex) { }
+ }
+ } else {
+ // Check if a peer server is connected to this agent
+ var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
+ if (routing != null) {
+ // Check if we have permission to send a message to that node
+ var rights = user.links[routing.meshid];
+ if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission
+ command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
+ command.rights = rights.rights; // Add user rights flags to the message
+ obj.parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
try {
// Check if the user is logged in
if (user == null) { try { obj.ws.close(); } catch (e) { } return; }
@@ -303,37 +337,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'msg':
{
- // Route a message.
- // This this command has a nodeid, that is the target.
- if (obj.common.validateString(command.nodeid, 8, 128) == false) return;
- var splitnodeid = command.nodeid.split('/');
- // Check that we are in the same domain and the user has rights over this node.
- if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domain.id)) {
- // See if the node is connected
- var agent = obj.parent.wsagents[command.nodeid];
- if (agent != null) {
- // Check if we have permission to send a message to that node
- var rights = user.links[agent.dbMeshKey];
- if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission, 256 is desktop read only
- command.sessionid = ws.sessionId; // Set the session id, required for responses.
- command.rights = rights.rights; // Add user rights flags to the message
- delete command.nodeid; // Remove the nodeid since it's implyed.
- try { agent.send(JSON.stringify(command)); } catch (ex) { }
- }
- } else {
- // Check if a peer server is connected to this agent
- var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
- if (routing != null) {
- // Check if we have permission to send a message to that node
- var rights = user.links[routing.meshid];
- if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission
- command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
- command.rights = rights.rights; // Add user rights flags to the message
- obj.parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
- }
- }
- }
- }
+ // Route this command to a target node
+ routeCommandToNode(command);
break;
}
case 'events':
@@ -601,19 +606,38 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
{
// Send a notification message to a user
if ((user.siteadmin & 2) == 0) break;
- if (obj.common.validateString(command.userid, 1, 2048) == false) break;
- // Create the notification message
- var notification = {
- "action": "msg", "type": "notify", "value": "" + user.name + ": Chat Request, Click here to accept.", "userid": user._id, "username": user.name, "tag": 'meshmessenger/' + encodeURIComponent(command.userid) + '/' + encodeURIComponent(user._id) };
+ // Setup a user-to-user session
+ if (obj.common.validateString(command.userid, 1, 2048)) {
- // Get the list of sessions for this user
- var sessions = obj.parent.wssessions[command.userid];
- if (sessions != null) { for (i in sessions) { try { sessions[i].send(JSON.stringify(notification)); } catch (ex) { } } }
+ // Create the notification message
+ var notification = {
+ "action": "msg", "type": "notify", "value": "" + user.name + ": Chat Request, Click here to accept.", "userid": user._id, "username": user.name, "tag": 'meshmessenger/' + encodeURIComponent(command.userid) + '/' + encodeURIComponent(user._id)
+ };
- if (obj.parent.parent.multiServer != null) {
- // TODO: Add multi-server support
+ // Get the list of sessions for this user
+ var sessions = obj.parent.wssessions[command.userid];
+ if (sessions != null) { for (i in sessions) { try { sessions[i].send(JSON.stringify(notification)); } catch (ex) { } } }
+
+ if (obj.parent.parent.multiServer != null) {
+ // TODO: Add multi-server support
+ }
}
+
+ // Setup a user-to-node session
+ if (obj.common.validateString(command.nodeid, 1, 2048)) {
+ if (obj.args.lanonly == true) { return; } // User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server??
+
+ // Create the server url
+ var httpsPort = ((obj.parent.args.aliasport == null) ? obj.parent.args.port : obj.parent.args.aliasport); // Use HTTPS alias port is specified
+ var xdomain = (domain.dns == null) ? domain.id : '';
+ if (xdomain != '') xdomain += "/";
+ var url = "http" + (obj.args.notls ? '' : 's') + "://" + obj.parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger.htm?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name);
+
+ // Create the notification message
+ routeCommandToNode({ "action": "openUrl", "nodeid": command.nodeid, "userid": user._id, "username": user.name, "url": url });
+ }
+
break;
}
case 'serverversion':
diff --git a/package.json b/package.json
index fdef964a..f8e5aa37 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "meshcentral",
- "version": "0.2.4-b",
+ "version": "0.2.4-e",
"keywords": [
"Remote Management",
"Intel AMT",
diff --git a/public/images/icon-notify.png b/public/images/icon-notify.png
new file mode 100644
index 00000000..0ce5fcf0
Binary files /dev/null and b/public/images/icon-notify.png differ
diff --git a/public/images/icon-url.png b/public/images/icon-url.png
new file mode 100644
index 00000000..f95a3e66
Binary files /dev/null and b/public/images/icon-url.png differ
diff --git a/public/images/icon-url2.png b/public/images/icon-url2.png
new file mode 100644
index 00000000..db9257fd
Binary files /dev/null and b/public/images/icon-url2.png differ
diff --git a/views/default.handlebars b/views/default.handlebars
index bcdf2659..d6410bd8 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -447,8 +447,11 @@
|