2017-08-28 12:27:45 -04:00
/ *
2020-01-02 21:30:12 -05:00
Copyright 2018 - 2020 Intel Corporation
2017-08-28 12:27:45 -04:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
2018-01-16 20:30:34 -05:00
http : //www.apache.org/licenses/LICENSE-2.0
2017-08-28 12:27:45 -04:00
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2018-09-20 14:45:12 -04:00
process . on ( 'uncaughtException' , function ( ex ) {
2020-01-07 16:04:36 -05:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : "uncaughtException1: " + ex } ) ;
2018-09-20 14:45:12 -04:00
} ) ;
2018-11-28 13:50:25 -05:00
// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
2018-11-26 20:12:27 -05:00
//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
2018-09-20 14:45:12 -04:00
2018-11-27 20:13:01 -05:00
// Mesh Rights
2019-06-14 19:33:53 -04:00
var MNG _ERROR = 65 ;
2018-12-29 18:24:33 -05:00
var MESHRIGHT _EDITMESH = 1 ;
var MESHRIGHT _MANAGEUSERS = 2 ;
var MESHRIGHT _MANAGECOMPUTERS = 4 ;
var MESHRIGHT _REMOTECONTROL = 8 ;
var MESHRIGHT _AGENTCONSOLE = 16 ;
var MESHRIGHT _SERVERFILES = 32 ;
var MESHRIGHT _WAKEDEVICE = 64 ;
var MESHRIGHT _SETNOTES = 128 ;
var MESHRIGHT _REMOTEVIEW = 256 ;
2019-03-01 14:47:58 -05:00
var MESHRIGHT _NOTERMINAL = 512 ;
var MESHRIGHT _NOFILES = 1024 ;
var MESHRIGHT _NOAMT = 2048 ;
2019-03-09 17:28:08 -05:00
var MESHRIGHT _LIMITEDINPUT = 4096 ;
2020-03-06 17:06:33 -05:00
var MESHRIGHT _LIMITEVENTS = 8192 ;
var MESHRIGHT _CHATNOTIFY = 16384 ;
var MESHRIGHT _UNINSTALL = 32768 ;
var MESHRIGHT _NODESKTOP = 65536 ;
2018-11-27 20:13:01 -05:00
2019-10-10 14:13:25 -04:00
function createMeshCore ( agent ) {
2018-08-31 18:23:42 -04:00
var obj = { } ;
2020-01-25 06:08:01 -05:00
if ( process . platform == 'win32' && require ( 'user-sessions' ) . isRoot ( ) ) {
2020-01-06 14:00:37 -05:00
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2020-01-22 20:15:59 -05:00
try {
var writtenSize = 0 , actualSize = Math . floor ( require ( 'fs' ) . statSync ( process . execPath ) . size / 1024 ) ;
2020-09-30 19:59:34 -04:00
try { writtenSize = require ( 'win-registry' ) . QueryKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' ) ; } catch ( e ) { }
if ( writtenSize != actualSize ) { try { require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' , actualSize ) ; } catch ( e ) { } }
} catch ( x ) { }
2020-01-21 18:44:24 -05:00
// Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
2020-01-22 20:15:59 -05:00
try {
var meshCheck = false ;
2020-09-30 19:59:34 -04:00
try { meshCheck = require ( 'service-manager' ) . manager . getService ( 'Mesh Agent' ) . isMe ( ) ; } catch ( e ) { }
2020-01-22 20:15:59 -05:00
if ( meshCheck && require ( 'win-bcd' ) . isSafeModeService && ! require ( 'win-bcd' ) . isSafeModeService ( 'Mesh Agent' ) ) { require ( 'win-bcd' ) . enableSafeModeService ( 'Mesh Agent' ) ; }
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2020-01-06 14:00:37 -05:00
}
2018-09-14 20:42:39 -04:00
2019-10-10 14:13:25 -04:00
if ( process . platform == 'darwin' && ! process . versions ) {
2019-05-13 17:27:40 -04:00
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
var child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
child . stdin . write ( "cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); " ) ;
child . stdin . write ( " if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }" ) ;
child . stdin . write ( " else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n" ) ;
child . waitExit ( ) ;
2019-10-10 14:13:25 -04:00
if ( child . stdout . str . trim ( ) == 'Crashed' ) {
2019-05-13 17:27:40 -04:00
child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
child . stdin . write ( "launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n" ) ;
child . waitExit ( ) ;
2019-10-10 14:13:25 -04:00
if ( parseInt ( child . stdout . str . trim ( ) ) == process . pid ) {
2019-05-13 17:27:40 -04:00
// The currently running MeshAgent is us, so we can continue with the update
var plist = require ( 'fs' ) . readFileSync ( '/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist' ) . toString ( ) ;
var tokens = plist . split ( '<key>KeepAlive</key>' ) ;
2019-10-10 14:13:25 -04:00
if ( tokens [ 1 ] . split ( '>' ) [ 0 ] . split ( '<' ) [ 1 ] == 'dict' ) {
2019-05-13 17:27:40 -04:00
var tmp = tokens [ 1 ] . split ( '</dict>' ) ;
tmp . shift ( ) ;
tokens [ 1 ] = '\n <true/>' + tmp . join ( '</dict>' ) ;
tokens = tokens . join ( '<key>KeepAlive</key>' ) ;
require ( 'fs' ) . writeFileSync ( '/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist' , tokens ) ;
var fix = '' ;
fix += ( "function macosRepair()\n" ) ;
fix += ( "{\n" ) ;
fix += ( " var child = require('child_process').execFile('/bin/sh', ['sh']);\n" ) ;
fix += ( " child.stdout.str = '';\n" ) ;
fix += ( " child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n" ) ;
fix += ( " child.stderr.on('data', function (chunk) { });\n" ) ;
fix += ( " child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n" ) ;
fix += ( " child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n" ) ;
fix += ( " child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n" ) ;
2019-05-13 17:39:12 -04:00
fix += ( " child.stdin.write('rm " + process . cwd ( ) + "/macosRepair.js\\n');\n" ) ;
2019-05-13 17:27:40 -04:00
fix += ( " child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n" ) ;
fix += ( " child.waitExit();\n" ) ;
fix += ( "}\n" ) ;
fix += ( "macosRepair();\n" ) ;
fix += ( "process.exit();\n" ) ;
require ( 'fs' ) . writeFileSync ( process . cwd ( ) + '/macosRepair.js' , fix ) ;
var plist = '<?xml version="1.0" encoding="UTF-8"?>\n' ;
plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' ;
plist += '<plist version="1.0">\n' ;
plist += ' <dict>\n' ;
plist += ' <key>Label</key>\n' ;
plist += ( ' <string>meshagentRepair</string>\n' ) ;
plist += ' <key>ProgramArguments</key>\n' ;
plist += ' <array>\n' ;
plist += ( ' <string>' + process . execPath + '</string>\n' ) ;
plist += ' <string>macosRepair.js</string>\n' ;
plist += ' </array>\n' ;
plist += ' <key>WorkingDirectory</key>\n' ;
plist += ( ' <string>' + process . cwd ( ) + '</string>\n' ) ;
plist += ' <key>RunAtLoad</key>\n' ;
plist += ' <true/>\n' ;
plist += ' </dict>\n' ;
plist += '</plist>' ;
require ( 'fs' ) . writeFileSync ( '/Library/LaunchDaemons/meshagentRepair.plist' , plist ) ;
child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
child . stdin . write ( "launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n" ) ;
child . waitExit ( ) ;
}
}
}
}
2019-06-14 19:33:53 -04:00
// Create Secure IPC for Diagnostic Agent Communications
obj . DAIPC = require ( 'net' ) . createServer ( ) ;
2020-09-30 19:59:34 -04:00
if ( process . platform != 'win32' ) { try { require ( 'fs' ) . unlinkSync ( process . cwd ( ) + '/DAIPC' ) ; } catch ( e ) { } }
2019-06-14 19:33:53 -04:00
obj . DAIPC . IPCPATH = process . platform == 'win32' ? ( '\\\\.\\pipe\\' + require ( '_agentNodeId' ) ( ) + '-DAIPC' ) : ( process . cwd ( ) + '/DAIPC' ) ;
2020-10-01 18:45:44 -04:00
try { obj . DAIPC . listen ( { path : obj . DAIPC . IPCPATH , writableAll : true } ) ; } catch ( e ) { }
2019-10-10 14:13:25 -04:00
obj . DAIPC . on ( 'connection' , function ( c ) {
c . _send = function ( j ) {
2019-06-14 19:33:53 -04:00
var data = JSON . stringify ( j ) ;
var packet = Buffer . alloc ( data . length + 4 ) ;
packet . writeUInt32LE ( data . length + 4 , 0 ) ;
Buffer . from ( data ) . copy ( packet , 4 ) ;
2020-09-30 19:59:34 -04:00
this . write ( packet ) ;
2019-06-14 19:33:53 -04:00
} ;
this . _daipc = c ;
c . parent = this ;
2020-10-01 13:30:26 -04:00
c . on ( 'end' , function ( ) {
this . end ( ) ; // TODO: Must call end() on self to close the named pipe correctly.
this . parent . _daipc = null ;
if ( this . _registered != null ) { try { mesh . SendCommand ( { action : 'sessions' , type : 'app' , value : { } } ) ; } catch ( e ) { } }
} ) ;
2019-10-10 14:13:25 -04:00
c . on ( 'data' , function ( chunk ) {
2019-06-14 19:33:53 -04:00
if ( chunk . length < 4 ) { this . unshift ( chunk ) ; return ; }
var len = chunk . readUInt32LE ( 0 ) ;
if ( len > 8192 ) { this . parent . _daipc = null ; this . end ( ) ; return ; }
if ( chunk . length < len ) { this . unshift ( chunk ) ; return ; }
2019-10-10 14:13:25 -04:00
2019-06-14 19:33:53 -04:00
var data = chunk . slice ( 4 , len ) ;
2020-09-30 19:59:34 -04:00
try { data = JSON . parse ( data . toString ( ) ) ; } catch ( e ) { }
if ( ( data == null ) || ( typeof data . cmd != 'string' ) ) return ;
2019-06-14 19:33:53 -04:00
2019-10-10 14:13:25 -04:00
try {
switch ( data . cmd ) {
2020-10-01 18:45:44 -04:00
case 'requesthelp' :
if ( this . _registered == null ) return ;
sendConsoleText ( 'Request Help (' + this . _registered + '): ' + data . value ) ;
var help = { } ;
help [ this . _registered ] = data . value ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'help' , value : help } ) ; } catch ( e ) { }
MeshServerLogEx ( 98 , [ this . _registered , data . value ] , "Help Requested, user: " + this . _registered + ", details: " + data . value , null ) ;
break ;
case 'cancelhelp' :
if ( this . _registered == null ) return ;
sendConsoleText ( 'Cancel Help (' + this . _registered + ')' ) ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'help' , value : { } } ) ; } catch ( e ) { }
break ;
2020-10-01 13:30:26 -04:00
case 'register' :
if ( typeof data . value == 'string' ) {
this . _registered = data . value ;
var apps = { } ;
apps [ data . value ] = 1 ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'app' , value : apps } ) ; } catch ( e ) { }
this . _send ( { cmd : 'serverstate' , value : meshServerConnectionState , url : require ( 'MeshAgent' ) . ConnectedServer } ) ;
}
break ;
2019-06-14 19:33:53 -04:00
case 'query' :
2019-10-10 14:13:25 -04:00
switch ( data . value ) {
2019-06-14 19:33:53 -04:00
case 'connection' :
data . result = require ( 'MeshAgent' ) . ConnectedServer ;
this . _send ( data ) ;
2020-05-29 16:07:15 -04:00
break ;
2020-05-28 16:30:28 -04:00
case 'descriptors' :
require ( 'ChainViewer' ) . getSnapshot ( ) . then ( function ( f )
{
this . tag . payload . result = f ;
this . tag . ipc . _send ( this . tag . payload ) ;
} ) . parentPromise . tag = { ipc : this , payload : data } ;
2019-06-14 19:33:53 -04:00
break ;
}
break ;
}
}
2020-09-30 19:59:34 -04:00
catch ( e ) { this . parent . _daipc = null ; this . end ( ) ; return ; }
2019-06-14 19:33:53 -04:00
} ) ;
} ) ;
2019-10-10 14:13:25 -04:00
function diagnosticAgent _uninstall ( ) {
2019-06-14 19:33:53 -04:00
require ( 'service-manager' ) . manager . uninstallService ( 'meshagentDiagnostic' ) ;
require ( 'task-scheduler' ) . delete ( 'meshagentDiagnostic/periodicStart' ) ;
} ;
2019-10-10 14:13:25 -04:00
function diagnosticAgent _installCheck ( install ) {
try {
2019-06-14 19:33:53 -04:00
var diag = require ( 'service-manager' ) . manager . getService ( 'meshagentDiagnostic' ) ;
return ( diag ) ;
}
2019-10-10 14:13:25 -04:00
catch ( e ) {
2019-06-14 19:33:53 -04:00
}
if ( ! install ) { return ( null ) ; }
var svc = null ;
2019-10-10 14:13:25 -04:00
try {
2019-06-14 19:33:53 -04:00
require ( 'service-manager' ) . manager . installService (
{
name : 'meshagentDiagnostic' ,
2020-01-07 16:04:36 -05:00
displayName : "Mesh Agent Diagnostic Service" ,
description : "Mesh Agent Diagnostic Service" ,
2019-06-14 19:33:53 -04:00
servicePath : process . execPath ,
parameters : [ '-recovery' ]
//files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
} ) ;
svc = require ( 'service-manager' ) . manager . getService ( 'meshagentDiagnostic' ) ;
}
2019-10-10 14:13:25 -04:00
catch ( e ) {
2019-06-14 19:33:53 -04:00
return ( null ) ;
}
var proxyConfig = require ( 'global-tunnel' ) . proxyConfig ;
var cert = require ( 'MeshAgent' ) . GenerateAgentCertificate ( 'CN=MeshNodeDiagnosticCertificate' ) ;
var nodeid = require ( 'tls' ) . loadCertificate ( cert . root ) . getKeyHash ( ) . toString ( 'base64' ) ;
ddb = require ( 'SimpleDataStore' ) . Create ( svc . appWorkingDirectory ( ) . replace ( '\\' , '/' ) + '/meshagentDiagnostic.db' ) ;
ddb . Put ( 'disableUpdate' , '1' ) ;
ddb . Put ( 'MeshID' , Buffer . from ( require ( 'MeshAgent' ) . ServerInfo . MeshID , 'hex' ) ) ;
ddb . Put ( 'ServerID' , require ( 'MeshAgent' ) . ServerInfo . ServerID ) ;
ddb . Put ( 'MeshServer' , require ( 'MeshAgent' ) . ServerInfo . ServerUri ) ;
if ( cert . root . pfx ) { ddb . Put ( 'SelfNodeCert' , cert . root . pfx ) ; }
if ( cert . tls ) { ddb . Put ( 'SelfNodeTlsCert' , cert . tls . pfx ) ; }
2019-10-10 14:13:25 -04:00
if ( proxyConfig ) {
2019-06-14 19:33:53 -04:00
ddb . Put ( 'WebProxy' , proxyConfig . host + ':' + proxyConfig . port ) ;
2020-05-07 02:23:27 -04:00
} else {
2019-06-14 19:33:53 -04:00
ddb . Put ( 'ignoreProxyFile' , '1' ) ;
}
require ( 'MeshAgent' ) . SendCommand ( { action : 'diagnostic' , value : { command : 'register' , value : nodeid } } ) ;
2020-01-07 16:04:36 -05:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : "Diagnostic Agent Registered [" + nodeid . length + "/" + nodeid + "]" } ) ;
2019-06-14 19:33:53 -04:00
delete ddb ;
// Set a recurrent task, to run the Diagnostic Agent every 2 days
2019-10-10 14:13:25 -04:00
require ( 'task-scheduler' ) . create ( { name : 'meshagentDiagnostic/periodicStart' , daily : 2 , time : require ( 'tls' ) . generateRandomInteger ( '0' , '23' ) + ':' + require ( 'tls' ) . generateRandomInteger ( '0' , '59' ) . padStart ( 2 , '0' ) , service : 'meshagentDiagnostic' } ) ;
2019-06-14 19:33:53 -04:00
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
return ( svc ) ;
}
2020-08-31 19:45:45 -04:00
// Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
if ( ( require ( 'fs' ) . existsSync ( process . cwd ( ) + 'batterystate.txt' ) ) && ( require ( 'fs' ) . watch != null ) ) {
2020-08-28 23:01:06 -04:00
// Setup manual battery monitoring
2020-08-31 19:45:45 -04:00
require ( 'MeshAgent' ) . _batteryFileWatcher = require ( 'fs' ) . watch ( process . cwd ( ) , function ( ) {
2020-08-28 23:01:06 -04:00
if ( require ( 'MeshAgent' ) . _batteryFileTimer != null ) return ;
require ( 'MeshAgent' ) . _batteryFileTimer = setTimeout ( function ( ) {
try {
require ( 'MeshAgent' ) . _batteryFileTimer = null ;
2020-08-31 19:45:45 -04:00
var data = null ;
2020-09-30 19:59:34 -04:00
try { data = require ( 'fs' ) . readFileSync ( process . cwd ( ) + 'batterystate.txt' ) . toString ( ) ; } catch ( e ) { }
2020-08-31 19:45:45 -04:00
if ( ( data != null ) && ( data . length < 10 ) ) {
2020-08-28 23:01:06 -04:00
data = data . split ( ',' ) ;
2020-08-31 19:45:45 -04:00
if ( ( data . length == 2 ) && ( ( data [ 0 ] == 'ac' ) || ( data [ 0 ] == 'dc' ) ) ) {
var level = parseInt ( data [ 1 ] ) ;
if ( ( level >= 0 ) && ( level <= 100 ) ) { require ( 'MeshAgent' ) . SendCommand ( { action : 'battery' , state : data [ 0 ] , level : level } ) ; }
}
2020-08-28 23:01:06 -04:00
}
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2020-08-28 23:01:06 -04:00
} , 1000 ) ;
2020-05-31 14:24:34 -04:00
} ) ;
2020-08-28 23:01:06 -04:00
} else {
// Setup normal battery monitoring
if ( require ( 'identifiers' ) . isBatteryPowered && require ( 'identifiers' ) . isBatteryPowered ( ) ) {
require ( 'MeshAgent' ) . _battLevelChanged = function _battLevelChanged ( val ) {
_battLevelChanged . self . _currentBatteryLevel = val ;
_battLevelChanged . self . SendCommand ( { action : 'battery' , state : _battLevelChanged . self . _currentPowerState , level : val } ) ;
} ;
require ( 'MeshAgent' ) . _battLevelChanged . self = require ( 'MeshAgent' ) ;
require ( 'MeshAgent' ) . _powerChanged = function _powerChanged ( val ) {
_powerChanged . self . _currentPowerState = ( val == 'AC' ? 'ac' : 'dc' ) ;
_powerChanged . self . SendCommand ( { action : 'battery' , state : ( val == 'AC' ? 'ac' : 'dc' ) , level : _powerChanged . self . _currentBatteryLevel } ) ;
} ;
require ( 'MeshAgent' ) . _powerChanged . self = require ( 'MeshAgent' ) ;
require ( 'MeshAgent' ) . on ( 'Connected' , function ( status ) {
if ( status == 0 ) {
require ( 'power-monitor' ) . removeListener ( 'acdc' , this . _powerChanged ) ;
require ( 'power-monitor' ) . removeListener ( 'batteryLevel' , this . _battLevelChanged ) ;
2020-08-31 19:45:45 -04:00
} else {
2020-08-28 23:01:06 -04:00
require ( 'power-monitor' ) . on ( 'acdc' , this . _powerChanged ) ;
require ( 'power-monitor' ) . on ( 'batteryLevel' , this . _battLevelChanged ) ;
}
} ) ;
}
2020-08-31 19:45:45 -04:00
}
2020-05-31 14:24:34 -04:00
2018-09-20 14:45:12 -04:00
/ *
2018-08-31 18:23:42 -04:00
function borderController ( ) {
this . container = null ;
this . Start = function Start ( user ) {
if ( this . container == null ) {
if ( process . platform == 'win32' ) {
2018-09-21 19:34:35 -04:00
try {
this . container = require ( 'ScriptContainer' ) . Create ( { processIsolation : 1 , sessionId : user . SessionId } ) ;
2020-09-30 19:59:34 -04:00
} catch ( e ) {
2018-09-21 19:34:35 -04:00
this . container = require ( 'ScriptContainer' ) . Create ( { processIsolation : 1 } ) ;
}
} else {
2018-08-31 18:23:42 -04:00
this . container = require ( 'ScriptContainer' ) . Create ( { processIsolation : 1 , sessionId : user . uid } ) ;
}
this . container . parent = this ;
this . container . addModule ( 'monitor-info' , getJSModule ( 'monitor-info' ) ) ;
this . container . addModule ( 'monitor-border' , getJSModule ( 'monitor-border' ) ) ;
this . container . addModule ( 'promise' , getJSModule ( 'promise' ) ) ;
this . container . once ( 'exit' , function ( code ) { sendConsoleText ( 'Border Process Exited with code: ' + code ) ; this . parent . container = this . parent . _container = null ; } ) ;
this . container . ExecuteString ( "var border = require('monitor-border'); border.Start();" ) ;
2018-08-29 21:47:22 -04:00
}
}
2018-08-31 18:23:42 -04:00
this . Stop = function Stop ( ) {
if ( this . container != null ) {
this . _container = this . container ;
this . _container . parent = this ;
this . container = null ;
this . _container . exit ( ) ;
}
2018-08-29 21:47:22 -04:00
}
}
2018-09-21 19:34:35 -04:00
obj . borderManager = new borderController ( ) ;
2018-09-20 14:45:12 -04:00
* /
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
2020-04-22 04:33:27 -04:00
var meshCoreObj = { action : 'coreinfo' , value : ( require ( 'MeshAgent' ) . coreHash ? ( 'MeshCore CRC-' + crc32c ( require ( 'MeshAgent' ) . coreHash ) ) : ( 'MeshCore v6' ) ) , caps : 14 } ; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
2020-04-08 22:16:33 -04:00
2018-11-25 15:59:42 -05:00
// Get the operating system description string
2020-09-30 19:59:34 -04:00
try { require ( 'os' ) . name ( ) . then ( function ( v ) { meshCoreObj . osdesc = v ; } ) ; } catch ( e ) { }
2018-08-29 21:47:22 -04:00
2017-08-28 12:27:45 -04:00
var meshServerConnectionState = 0 ;
var tunnels = { } ;
2018-09-21 19:34:35 -04:00
var lastMeInfo = null ;
2017-08-28 12:27:45 -04:00
var lastNetworkInfo = null ;
2017-08-28 15:48:53 -04:00
var lastPublicLocationInfo = null ;
2017-08-28 12:27:45 -04:00
var selfInfoUpdateTimer = null ;
var http = require ( 'http' ) ;
2017-10-23 17:09:58 -04:00
var net = require ( 'net' ) ;
2017-08-28 12:27:45 -04:00
var fs = require ( 'fs' ) ;
2017-10-15 02:22:19 -04:00
var rtc = require ( 'ILibWebRTC' ) ;
2019-03-05 02:48:45 -05:00
var amt = null ;
2018-04-11 16:49:05 -04:00
var processManager = require ( 'process-manager' ) ;
2017-10-04 23:35:52 -04:00
var wifiScannerLib = null ;
var wifiScanner = null ;
2017-12-12 19:04:54 -05:00
var networkMonitor = null ;
2018-01-09 23:13:41 -05:00
var amtscanner = null ;
2018-01-18 18:43:43 -05:00
var nextTunnelIndex = 1 ;
2019-02-07 18:00:10 -05:00
var amtPolicy = null ;
2019-09-17 16:11:22 -04:00
var apftunnel = null ;
2020-07-28 21:45:17 -04:00
var tunnelUserCount = { terminal : { } , files : { } , tcp : { } , udp : { } , msg : { } } ; // List of userid->count sessions for terminal, files and TCP/UDP routing
2018-09-27 19:17:05 -04:00
2019-07-29 19:35:48 -04:00
// Add to the server event log
function MeshServerLog ( msg , state ) {
2020-01-07 16:04:36 -05:00
if ( typeof msg == 'string' ) { msg = { action : 'log' , msg : msg } ; } else { msg . action = 'log' ; }
2019-07-29 19:35:48 -04:00
if ( state ) {
if ( state . userid ) { msg . userid = state . userid ; }
if ( state . username ) { msg . username = state . username ; }
if ( state . sessionid ) { msg . sessionid = state . sessionid ; }
2020-09-08 21:59:58 -04:00
if ( state . remoteaddr ) { msg . remoteaddr = state . remoteaddr ; }
}
mesh . SendCommand ( msg ) ;
}
// Add to the server event log, use internationalized events
function MeshServerLogEx ( id , args , msg , state ) {
var msg = { action : 'log' , msgid : id , msgArgs : args , msg : msg } ;
if ( state ) {
if ( state . userid ) { msg . userid = state . userid ; }
if ( state . username ) { msg . username = state . username ; }
if ( state . sessionid ) { msg . sessionid = state . sessionid ; }
if ( state . remoteaddr ) { msg . remoteaddr = state . remoteaddr ; }
2019-07-29 19:35:48 -04:00
}
mesh . SendCommand ( msg ) ;
}
2018-09-27 19:17:05 -04:00
// If we are running in Duktape, agent will be null
if ( agent == null ) {
// Running in native agent, Import libraries
db = require ( 'SimpleDataStore' ) . Shared ( ) ;
sha = require ( 'SHA256Stream' ) ;
mesh = require ( 'MeshAgent' ) ;
childProcess = require ( 'child_process' ) ;
if ( mesh . hasKVM == 1 ) { // if the agent is compiled with KVM support
// Check if this computer supports a desktop
2020-04-24 01:42:45 -04:00
try
{
if ( ( process . platform == 'win32' ) || ( process . platform == 'darwin' ) || ( require ( 'monitor-info' ) . kvm _x11 _support ) )
{
meshCoreObj . caps |= 1 ;
}
else if ( process . platform == 'linux' || process . platform == 'freebsd' )
{
require ( 'monitor-info' ) . on ( 'kvmSupportDetected' , function ( value )
{
meshCoreObj . caps |= 1 ;
mesh . SendCommand ( meshCoreObj ) ;
} ) ;
}
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2018-09-27 19:17:05 -04:00
}
} else {
// Running in nodejs
2018-11-25 15:59:42 -05:00
meshCoreObj . value += '-NodeJS' ;
meshCoreObj . caps = 8 ;
2018-09-27 19:17:05 -04:00
mesh = agent . getMeshApi ( ) ;
}
2019-06-14 19:33:53 -04:00
mesh . DAIPC = obj . DAIPC ;
2018-01-09 23:13:41 -05:00
/ *
var AMTScanner = require ( "AMTScanner" ) ;
var scan = new AMTScanner ( ) ;
scan . on ( "found" , function ( data ) {
if ( typeof data === 'string' ) {
console . log ( data ) ;
} else {
console . log ( JSON . stringify ( data , null , " " ) ) ;
}
} ) ;
scan . scan ( "10.2.55.140" , 1000 ) ;
scan . scan ( "10.2.55.139-10.2.55.145" , 1000 ) ;
scan . scan ( "10.2.55.128/25" , 2000 ) ;
* /
2018-01-16 20:30:34 -05:00
/ *
2017-12-12 19:04:54 -05:00
// Try to load up the network monitor
try {
networkMonitor = require ( 'NetworkMonitor' ) ;
networkMonitor . on ( 'change' , function ( ) { sendNetworkUpdateNagle ( ) ; } ) ;
networkMonitor . on ( 'add' , function ( addr ) { sendNetworkUpdateNagle ( ) ; } ) ;
networkMonitor . on ( 'remove' , function ( addr ) { sendNetworkUpdateNagle ( ) ; } ) ;
} catch ( e ) { networkMonitor = null ; }
2018-01-16 20:30:34 -05:00
* /
2018-01-09 23:13:41 -05:00
// Try to load up the Intel AMT scanner
try {
var AMTScannerModule = require ( 'amt-scanner' ) ;
amtscanner = new AMTScannerModule ( ) ;
//amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
2020-09-30 19:59:34 -04:00
} catch ( e ) { amtscanner = null ; }
2018-09-27 19:17:05 -04:00
// Fetch the SMBios Tables
var SMBiosTables = null ;
var SMBiosTablesRaw = null ;
2018-11-25 15:59:42 -05:00
try {
2018-12-30 16:32:40 -05:00
var SMBiosModule = null ;
2020-09-30 19:59:34 -04:00
try { SMBiosModule = require ( 'smbios' ) ; } catch ( e ) { }
2018-12-30 16:32:40 -05:00
if ( SMBiosModule != null ) {
SMBiosModule . get ( function ( data ) {
if ( data != null ) {
SMBiosTablesRaw = data ;
SMBiosTables = require ( 'smbios' ) . parse ( data )
2020-01-07 16:04:36 -05:00
if ( mesh . isControlChannelConnected ) { mesh . SendCommand ( { action : 'smbios' , value : SMBiosTablesRaw } ) ; }
2018-12-30 16:32:40 -05:00
2019-11-28 15:27:44 -05:00
// If SMBios tables say that Intel AMT is present, try to connect MEI
2020-08-11 21:38:38 -04:00
if ( SMBiosTables . amtInfo && ( SMBiosTables . amtInfo . AMT == true ) )
{
2019-03-05 02:48:45 -05:00
var amtmodule = require ( 'amt-manage' ) ;
2019-11-28 15:34:45 -05:00
amt = new amtmodule ( mesh , db , false ) ;
2020-08-11 21:38:38 -04:00
amt . on ( 'portBinding_LMS' , function ( map )
{
var j = { action : 'lmsinfo' , value : { ports : map . keys ( ) } } ;
mesh . SendCommand ( j ) ;
} ) ;
amt . on ( 'stateChange_LMS' , function ( v )
{
if ( ! meshCoreObj . intelamt ) { meshCoreObj . intelamt = { } ; }
switch ( v )
{
case 0 :
meshCoreObj . intelamt . microlms = 'DISABLED' ;
break ;
case 1 :
meshCoreObj . intelamt . microlms = 'CONNECTING' ;
break ;
case 2 :
meshCoreObj . intelamt . microlms = 'CONNECTED' ;
break ;
default :
break ;
}
mesh . SendCommand ( meshCoreObj ) ;
} ) ;
2019-03-05 02:48:45 -05:00
amt . onStateChange = function ( state ) { if ( state == 2 ) { sendPeriodicServerUpdate ( 1 ) ; } }
if ( amtPolicy != null ) { amt . setPolicy ( amtPolicy ) ; }
amt . start ( ) ;
}
2018-12-30 16:32:40 -05:00
}
} ) ;
}
2020-09-30 19:59:34 -04:00
} catch ( e ) { sendConsoleText ( "ex1: " + e ) ; }
2019-10-10 14:13:25 -04:00
2017-10-04 23:35:52 -04:00
// Try to load up the WIFI scanner
try {
2018-01-30 21:23:57 -05:00
var wifiScannerLib = require ( 'wifi-scanner' ) ;
2017-10-04 23:35:52 -04:00
wifiScanner = new wifiScannerLib ( ) ;
2019-03-05 02:48:45 -05:00
wifiScanner . on ( 'accessPoint' , function ( data ) { sendConsoleText ( "wifiScanner: " + data ) ; } ) ;
2020-09-30 19:59:34 -04:00
} catch ( e ) { wifiScannerLib = null ; wifiScanner = null ; }
2018-12-28 00:31:20 -05:00
2017-08-28 15:48:53 -04:00
// Get our location (lat/long) using our public IP address
2017-09-01 14:23:22 -04:00
var getIpLocationDataExInProgress = false ;
2017-10-23 17:09:58 -04:00
var getIpLocationDataExCounts = [ 0 , 0 ] ;
2017-08-28 15:48:53 -04:00
function getIpLocationDataEx ( func ) {
2017-09-01 14:23:22 -04:00
if ( getIpLocationDataExInProgress == true ) { return false ; }
2017-08-28 15:48:53 -04:00
try {
2017-09-01 14:23:22 -04:00
getIpLocationDataExInProgress = true ;
getIpLocationDataExCounts [ 0 ] ++ ;
2018-07-23 20:34:24 -04:00
var options = http . parseUri ( "http://ipinfo.io/json" ) ;
options . method = 'GET' ;
http . request ( options , function ( resp ) {
2018-01-04 15:15:21 -05:00
if ( resp . statusCode == 200 ) {
var geoData = '' ;
resp . data = function ( geoipdata ) { geoData += geoipdata ; } ;
resp . end = function ( ) {
var location = null ;
try {
if ( typeof geoData == 'string' ) {
var result = JSON . parse ( geoData ) ;
if ( result . ip && result . loc ) { location = result ; }
}
} catch ( e ) { }
if ( func ) { getIpLocationDataExCounts [ 1 ] ++ ; func ( location ) ; }
}
} else { func ( null ) ; }
getIpLocationDataExInProgress = false ;
} ) . end ( ) ;
2017-09-01 14:23:22 -04:00
return true ;
2017-08-28 15:48:53 -04:00
}
2017-09-01 14:23:22 -04:00
catch ( e ) { return false ; }
2017-08-28 15:48:53 -04:00
}
2019-10-10 14:13:25 -04:00
2017-08-28 15:48:53 -04:00
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
function clearGatewayMac ( str ) {
if ( str == null ) return null ;
var x = JSON . parse ( str ) ;
for ( var i in x . netif ) { if ( x . netif [ i ] . gatewaymac ) { delete x . netif [ i ] . gatewaymac } }
return JSON . stringify ( x ) ;
}
2019-10-10 14:13:25 -04:00
2017-08-28 15:48:53 -04:00
function getIpLocationData ( func ) {
// Get the location information for the cache if possible
var publicLocationInfo = db . Get ( 'publicLocationInfo' ) ;
if ( publicLocationInfo != null ) { publicLocationInfo = JSON . parse ( publicLocationInfo ) ; }
if ( publicLocationInfo == null ) {
// Nothing in the cache, fetch the data
getIpLocationDataEx ( function ( locationData ) {
2017-09-01 20:34:02 -04:00
if ( locationData != null ) {
publicLocationInfo = { } ;
publicLocationInfo . netInfoStr = lastNetworkInfo ;
publicLocationInfo . locationData = locationData ;
var x = db . Put ( 'publicLocationInfo' , JSON . stringify ( publicLocationInfo ) ) ; // Save to database
if ( func ) func ( locationData ) ; // Report the new location
} else {
if ( func ) func ( null ) ; // Report no location
}
2017-08-28 15:48:53 -04:00
} ) ;
} else {
// Check the cache
if ( clearGatewayMac ( publicLocationInfo . netInfoStr ) == clearGatewayMac ( lastNetworkInfo ) ) {
// Cache match
if ( func ) func ( publicLocationInfo . locationData ) ;
} else {
// Cache mismatch
getIpLocationDataEx ( function ( locationData ) {
2017-09-01 20:34:02 -04:00
if ( locationData != null ) {
publicLocationInfo = { } ;
publicLocationInfo . netInfoStr = lastNetworkInfo ;
publicLocationInfo . locationData = locationData ;
var x = db . Put ( 'publicLocationInfo' , JSON . stringify ( publicLocationInfo ) ) ; // Save to database
if ( func ) func ( locationData ) ; // Report the new location
} else {
if ( func ) func ( publicLocationInfo . locationData ) ; // Can't get new location, report the old location
}
2017-08-28 15:48:53 -04:00
} ) ;
}
}
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Polyfill String.endsWith
if ( ! String . prototype . endsWith ) {
String . prototype . endsWith = function ( searchString , position ) {
var subjectString = this . toString ( ) ;
if ( typeof position !== 'number' || ! isFinite ( position ) || Math . floor ( position ) !== position || position > subjectString . length ) { position = subjectString . length ; }
position -= searchString . length ;
var lastIndex = subjectString . lastIndexOf ( searchString , position ) ;
return lastIndex !== - 1 && lastIndex === position ;
} ;
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Polyfill path.join
obj . path = {
join : function ( ) {
var x = [ ] ;
for ( var i in arguments ) {
var w = arguments [ i ] ;
if ( w != null ) {
while ( w . endsWith ( '/' ) || w . endsWith ( '\\' ) ) { w = w . substring ( 0 , w . length - 1 ) ; }
2017-10-23 17:09:58 -04:00
if ( i != 0 ) {
while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; }
}
2017-08-28 12:27:45 -04:00
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
} ;
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Replace a string with a number if the string is an exact number
2017-09-20 17:44:22 -04:00
function toNumberIfNumber ( x ) { if ( ( typeof x == 'string' ) && ( + parseInt ( x ) === x ) ) { x = parseInt ( x ) ; } return x ; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert decimal to hex
function char2hex ( i ) { return ( i + 0x100 ) . toString ( 16 ) . substr ( - 2 ) . toUpperCase ( ) ; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert a raw string to a hex string
function rstr2hex ( input ) { var r = '' , i ; for ( i = 0 ; i < input . length ; i ++ ) { r += char2hex ( input . charCodeAt ( i ) ) ; } return r ; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert a buffer into a string
function buf2rstr ( buf ) { var r = '' ; for ( var i = 0 ; i < buf . length ; i ++ ) { r += String . fromCharCode ( buf [ i ] ) ; } return r ; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
function hex2rstr ( d ) {
if ( typeof d != "string" || d . length == 0 ) return '' ;
var r = '' , m = ( '' + d ) . match ( /../g ) , t ;
while ( t = m . shift ( ) ) r += String . fromCharCode ( '0x' + t ) ;
return r
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert an object to string with all functions
2018-03-12 21:16:06 -04:00
function objToString ( x , p , pad , ret ) {
2017-08-28 12:27:45 -04:00
if ( ret == undefined ) ret = '' ;
if ( p == undefined ) p = 0 ;
if ( x == null ) { return '[null]' ; }
if ( p > 8 ) { return '[...]' ; }
if ( x == undefined ) { return '[undefined]' ; }
if ( typeof x == 'string' ) { if ( p == 0 ) return x ; return '"' + x + '"' ; }
if ( typeof x == 'buffer' ) { return '[buffer]' ; }
if ( typeof x != 'object' ) { return x ; }
var r = '{' + ( ret ? '\r\n' : ' ' ) ;
2018-03-12 21:16:06 -04:00
for ( var i in x ) { if ( i != '_ObjectID' ) { r += ( addPad ( p + 2 , pad ) + i + ': ' + objToString ( x [ i ] , p + 2 , pad , ret ) + ( ret ? '\r\n' : ' ' ) ) ; } }
return r + addPad ( p , pad ) + '}' ;
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Return p number of spaces
function addPad ( p , ret ) { var r = '' ; for ( var i = 0 ; i < p ; i ++ ) { r += ret ; } return r ; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Split a string taking into account the quoats. Used for command line parsing
function splitArgs ( str ) {
var myArray = [ ] , myRegexp = /[^\s"]+|"([^"]*)"/gi ;
do { var match = myRegexp . exec ( str ) ; if ( match != null ) { myArray . push ( match [ 1 ] ? match [ 1 ] : match [ 0 ] ) ; } } while ( match != null ) ;
return myArray ;
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Parse arguments string array into an object
function parseArgs ( argv ) {
var results = { '_' : [ ] } , current = null ;
for ( var i = 1 , len = argv . length ; i < len ; i ++ ) {
var x = argv [ i ] ;
if ( x . length > 2 && x [ 0 ] == '-' && x [ 1 ] == '-' ) {
if ( current != null ) { results [ current ] = true ; }
current = x . substring ( 2 ) ;
} else {
if ( current != null ) { results [ current ] = toNumberIfNumber ( x ) ; current = null ; } else { results [ '_' ] . push ( toNumberIfNumber ( x ) ) ; }
}
}
if ( current != null ) { results [ current ] = true ; }
return results ;
}
2019-10-10 14:13:25 -04:00
2017-09-20 17:44:22 -04:00
// Get server target url with a custom path
function getServerTargetUrl ( path ) {
var x = mesh . ServerUrl ;
//sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
if ( x == null ) { return null ; }
if ( path == null ) { path = '' ; }
x = http . parseUri ( x ) ;
if ( x == null ) return null ;
return x . protocol + '//' + x . host + ':' + x . port + '/' + path ;
}
2019-10-10 14:13:25 -04:00
2017-09-20 17:44:22 -04:00
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
function getServerTargetUrlEx ( url ) {
if ( url . substring ( 0 , 2 ) == '*/' ) { return getServerTargetUrl ( url . substring ( 2 ) ) ; }
return url ;
}
2019-10-10 14:13:25 -04:00
2017-09-20 17:44:22 -04:00
// Send a wake-on-lan packet
function sendWakeOnLan ( hexMac ) {
2020-07-17 22:55:49 -04:00
hexMac = hexMac . split ( ':' ) . join ( '' ) ;
2017-09-20 17:44:22 -04:00
var count = 0 ;
try {
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
var magic = 'FFFFFFFFFFFF' ;
for ( var x = 1 ; x <= 16 ; ++ x ) { magic += hexMac ; }
var magicbin = Buffer . from ( magic , 'hex' ) ;
2019-10-10 14:13:25 -04:00
2020-07-17 22:55:49 -04:00
for ( var adapter in interfaces ) {
if ( interfaces . hasOwnProperty ( adapter ) ) {
for ( var i = 0 ; i < interfaces [ adapter ] . length ; ++ i ) {
2017-09-20 17:44:22 -04:00
var addr = interfaces [ adapter ] [ i ] ;
2020-07-17 22:55:49 -04:00
if ( ( addr . family == 'IPv4' ) && ( addr . mac != '00:00:00:00:00:00' ) ) {
try {
2020-06-25 22:14:29 -04:00
var socket = require ( 'dgram' ) . createSocket ( { type : 'udp4' } ) ;
socket . bind ( { address : addr . address } ) ;
socket . setBroadcast ( true ) ;
2020-08-05 18:26:46 -04:00
socket . setMulticastInterface ( addr . address ) ;
socket . setMulticastTTL ( 1 ) ;
2020-06-25 22:14:29 -04:00
socket . send ( magicbin , 7 , '255.255.255.255' ) ;
socket . descriptorMetadata = 'WoL (' + addr . address + ' => ' + hexMac + ')' ;
count ++ ;
}
2020-09-30 19:59:34 -04:00
catch ( e ) { }
2017-09-20 17:44:22 -04:00
}
}
}
}
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2017-09-20 17:44:22 -04:00
return count ;
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Handle a mesh agent command
function handleServerCommand ( data ) {
if ( typeof data == 'object' ) {
// If this is a console command, parse it and call the console handler
2017-08-28 15:48:53 -04:00
switch ( data . action ) {
case 'msg' : {
2018-04-11 16:49:05 -04:00
switch ( data . type ) {
case 'console' : { // Process a console command
if ( data . value && data . sessionid ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 17 , [ data . value ] , "Processing console command: " + data . value , data ) ;
2018-04-11 16:49:05 -04:00
var args = splitArgs ( data . value ) ;
processConsoleCommand ( args [ 0 ] . toLowerCase ( ) , parseArgs ( args ) , data . rights , data . sessionid ) ;
}
break ;
2017-08-28 15:48:53 -04:00
}
2018-04-11 16:49:05 -04:00
case 'tunnel' : {
if ( data . value != null ) { // Process a new tunnel connection request
// Create a new tunnel object
var xurl = getServerTargetUrlEx ( data . value ) ;
if ( xurl != null ) {
2020-06-17 13:28:54 -04:00
xurl = xurl . split ( '$' ) . join ( '%24' ) . split ( '@' ) . join ( '%40' ) ; // Escape the $ and @ characters
2018-04-11 16:49:05 -04:00
var woptions = http . parseUri ( xurl ) ;
2020-07-17 22:55:49 -04:00
woptions . perMessageDeflate = false ;
if ( typeof data . perMessageDeflate == 'boolean' ) { woptions . perMessageDeflate = data . perMessageDeflate ; }
2018-04-11 16:49:05 -04:00
woptions . rejectUnauthorized = 0 ;
2018-12-01 00:23:10 -05:00
//sendConsoleText(JSON.stringify(woptions));
2019-05-07 15:39:49 -04:00
//sendConsoleText('TUNNEL: ' + JSON.stringify(data));
2018-04-11 16:49:05 -04:00
var tunnel = http . request ( woptions ) ;
tunnel . upgrade = onTunnelUpgrade ;
2020-01-10 14:19:23 -05:00
tunnel . on ( 'error' , function ( e ) { sendConsoleText ( "ERROR: Unable to connect relay tunnel to: " + this . url + ", " + JSON . stringify ( e ) ) ; } ) ;
2018-04-11 16:49:05 -04:00
tunnel . sessionid = data . sessionid ;
tunnel . rights = data . rights ;
2019-04-12 17:19:03 -04:00
tunnel . consent = data . consent ;
2020-01-07 16:04:36 -05:00
tunnel . privacybartext = data . privacybartext ? data . privacybartext : "Sharing desktop with: {0}" ;
2020-08-07 18:01:28 -04:00
tunnel . username = data . username + ( data . guestname ? ( ' - ' + data . guestname ) : '' ) ;
tunnel . realname = ( data . realname ? data . realname : data . username ) + ( data . guestname ? ( ' - ' + data . guestname ) : '' ) ;
2019-07-29 19:35:48 -04:00
tunnel . userid = data . userid ;
tunnel . remoteaddr = data . remoteaddr ;
2018-04-11 16:49:05 -04:00
tunnel . state = 0 ;
tunnel . url = xurl ;
tunnel . protocol = 0 ;
2020-06-13 08:41:53 -04:00
tunnel . soptions = data . soptions ;
2018-04-11 16:49:05 -04:00
tunnel . tcpaddr = data . tcpaddr ;
tunnel . tcpport = data . tcpport ;
2019-05-05 01:53:57 -04:00
tunnel . udpaddr = data . udpaddr ;
tunnel . udpport = data . udpport ;
2018-04-11 16:49:05 -04:00
tunnel . end ( ) ;
// Put the tunnel in the tunnels list
2018-11-27 20:13:01 -05:00
var index = nextTunnelIndex ++ ;
2018-04-11 16:49:05 -04:00
tunnel . index = index ;
tunnels [ index ] = tunnel ;
2018-11-27 20:13:01 -05:00
//sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
2018-04-11 16:49:05 -04:00
}
}
break ;
}
2020-05-18 20:57:11 -04:00
case 'messagebox' : {
// Display a message box
if ( data . title && data . msg ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 18 , [ data . title , data . msg ] , "Displaying message box, title=" + data . title + ", message=" + data . msg , data ) ;
2020-05-18 20:57:11 -04:00
data . msg = data . msg . split ( '\r' ) . join ( '\\r' ) . split ( '\n' ) . join ( '\\n' ) ;
2020-09-30 19:59:34 -04:00
try { require ( 'message-box' ) . create ( data . title , data . msg , 120 ) ; } catch ( e ) { }
2020-05-18 20:57:11 -04:00
}
break ;
}
2018-04-11 16:49:05 -04:00
case 'ps' : {
2018-12-07 19:36:27 -05:00
// Return the list of running processes
2018-04-11 16:49:05 -04:00
if ( data . sessionid ) {
2019-07-31 19:49:23 -04:00
processManager . getProcesses ( function ( plist ) {
2020-01-07 16:04:36 -05:00
mesh . SendCommand ( { action : 'msg' , type : 'ps' , value : JSON . stringify ( plist ) , sessionid : data . sessionid } ) ;
2019-07-31 19:49:23 -04:00
} ) ;
2018-04-11 16:49:05 -04:00
}
break ;
}
case 'pskill' : {
2018-12-07 19:36:27 -05:00
// Kill a process
if ( data . value ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 19 , [ data . value ] , "Killing process " + data . value , data ) ;
2019-03-05 02:48:45 -05:00
try { process . kill ( data . value ) ; } catch ( e ) { sendConsoleText ( "pskill: " + JSON . stringify ( e ) ) ; }
2018-12-07 19:36:27 -05:00
}
break ;
}
2019-07-31 19:49:23 -04:00
case 'services' : {
// Return the list of installed services
var services = null ;
try { services = require ( 'service-manager' ) . manager . enumerateService ( ) ; } catch ( e ) { }
2020-01-07 16:04:36 -05:00
if ( services != null ) { mesh . SendCommand ( { action : 'msg' , type : 'services' , value : JSON . stringify ( services ) , sessionid : data . sessionid } ) ; }
2019-07-31 19:49:23 -04:00
break ;
}
case 'serviceStop' : {
// Stop a service
try {
var service = require ( 'service-manager' ) . manager . getService ( data . serviceName ) ;
if ( service != null ) { service . stop ( ) ; }
} catch ( e ) { }
break ;
}
case 'serviceStart' : {
// Start a service
try {
var service = require ( 'service-manager' ) . manager . getService ( data . serviceName ) ;
if ( service != null ) { service . start ( ) ; }
} catch ( e ) { }
break ;
}
case 'serviceRestart' : {
2019-08-08 14:15:44 -04:00
// Restart a service
2019-07-31 19:49:23 -04:00
try {
var service = require ( 'service-manager' ) . manager . getService ( data . serviceName ) ;
2019-08-08 14:15:44 -04:00
if ( service != null ) { service . restart ( ) ; }
2019-07-31 19:49:23 -04:00
} catch ( e ) { }
break ;
}
2019-12-10 13:24:10 -05:00
case 'deskBackground' :
{
2020-01-07 16:04:36 -05:00
// Toggle desktop background
try {
if ( process . platform == 'win32' ) {
var stype = require ( 'user-sessions' ) . getProcessOwnerName ( process . pid ) . tsid == 0 ? 1 : 0 ;
var sid = undefined ;
if ( stype == 1 ) {
if ( require ( 'MeshAgent' ) . _tsid != null ) {
stype = 5 ;
sid = require ( 'MeshAgent' ) . _tsid ;
}
2019-12-10 13:24:10 -05:00
}
2020-01-07 16:04:36 -05:00
var id = require ( 'user-sessions' ) . getProcessOwnerName ( process . pid ) . tsid == 0 ? 1 : 0 ;
var child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' ] , { type : stype , uid : sid } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
var current = child . stdout . str . trim ( ) ;
if ( current != '' ) { require ( 'MeshAgent' ) . _wallpaper = current ; }
child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' , current != '' ? '""' : require ( 'MeshAgent' ) . _wallpaper ] , { type : stype , uid : sid } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
} else {
var id = require ( 'user-sessions' ) . consoleUid ( ) ;
var current = require ( 'linux-gnome-helpers' ) . getDesktopWallpaper ( id ) ;
if ( current != '/dev/null' ) { require ( 'MeshAgent' ) . _wallpaper = current ; }
require ( 'linux-gnome-helpers' ) . setDesktopWallpaper ( id , current != '/dev/null' ? undefined : require ( 'MeshAgent' ) . _wallpaper ) ;
2019-12-10 13:24:10 -05:00
}
2020-01-07 16:04:36 -05:00
} catch ( e ) {
sendConsoleText ( e ) ;
2019-11-28 15:27:44 -05:00
}
2020-01-07 16:04:36 -05:00
break ;
2019-12-10 13:24:10 -05:00
}
2018-12-07 19:36:27 -05:00
case 'openUrl' : {
// Open a local web browser and return success/fail
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 20 , [ data . url ] , "Opening: " + data . url , data ) ;
2020-01-07 16:04:36 -05:00
sendConsoleText ( "OpenURL: " + data . url ) ;
if ( data . url ) { mesh . SendCommand ( { action : 'msg' , type : 'openUrl' , url : data . url , sessionid : data . sessionid , success : ( openUserDesktopUrl ( data . url ) != null ) } ) ; }
2018-04-11 16:49:05 -04:00
break ;
2017-08-28 15:48:53 -04:00
}
2019-02-17 00:16:39 -05:00
case 'getclip' : {
// Send the load clipboard back to the user
2019-03-19 12:54:52 -04:00
//sendConsoleText('getClip: ' + JSON.stringify(data));
2019-07-29 19:35:48 -04:00
if ( require ( 'MeshAgent' ) . isService ) {
require ( 'clipboard' ) . dispatchRead ( ) . then ( function ( str ) {
if ( str ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 21 , [ str . length ] , "Getting clipboard content, " + str . length + " byte(s)" , data ) ;
2020-08-19 19:29:21 -04:00
mesh . SendCommand ( { action : 'msg' , type : 'getclip' , sessionid : data . sessionid , data : str , tag : data . tag } ) ;
2019-07-29 19:35:48 -04:00
}
} ) ;
} else {
require ( "clipboard" ) . read ( ) . then ( function ( str ) {
if ( str ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 21 , [ str . length ] , "Getting clipboard content, " + str . length + " byte(s)" , data ) ;
2020-08-19 19:29:21 -04:00
mesh . SendCommand ( { action : 'msg' , type : 'getclip' , sessionid : data . sessionid , data : str , tag : data . tag } ) ;
2019-07-29 19:35:48 -04:00
}
} ) ;
2019-03-15 16:55:53 -04:00
}
2019-02-17 00:16:39 -05:00
break ;
}
case 'setclip' : {
// Set the load clipboard to a user value
2019-03-19 12:54:52 -04:00
//sendConsoleText('setClip: ' + JSON.stringify(data));
2019-07-29 19:35:48 -04:00
if ( typeof data . data == 'string' ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 22 , [ data . data . length ] , "Setting clipboard content, " + data . data . length + " byte(s)" , data ) ;
2019-12-12 20:45:42 -05:00
if ( require ( 'MeshAgent' ) . isService ) { require ( 'clipboard' ) . dispatchWrite ( data . data ) ; } else { require ( "clipboard" ) ( data . data ) ; } // Set the clipboard
2020-01-07 16:04:36 -05:00
mesh . SendCommand ( { action : 'msg' , type : 'setclip' , sessionid : data . sessionid , success : true } ) ;
2019-07-29 19:35:48 -04:00
}
2019-02-17 00:16:39 -05:00
break ;
}
2019-12-12 20:45:42 -05:00
case 'userSessions' : {
// Send back current user sessions list, this is Windows only.
//sendConsoleText('userSessions: ' + JSON.stringify(data));
if ( process . platform != 'win32' ) break ;
var p = require ( 'user-sessions' ) . enumerateUsers ( ) ;
p . sessionid = data . sessionid ;
2020-04-14 22:12:14 -04:00
p . then ( function ( u ) { mesh . SendCommand ( { action : 'msg' , type : 'userSessions' , sessionid : data . sessionid , data : u , tag : data . tag } ) ; } ) ;
2019-12-12 20:45:42 -05:00
break ;
}
2019-01-04 20:59:13 -05:00
default :
// Unknown action, ignore it.
break ;
2017-08-28 12:27:45 -04:00
}
2018-04-12 14:15:01 -04:00
break ;
2017-08-28 15:48:53 -04:00
}
2019-06-19 20:16:50 -04:00
case 'acmactivate' : {
2019-07-29 19:35:48 -04:00
if ( amt != null ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 23 , null , "Attempting Intel AMT ACM mode activation" , data ) ;
2019-07-29 19:35:48 -04:00
amt . setAcmResponse ( data ) ;
}
2019-06-19 20:16:50 -04:00
break ;
}
2017-08-28 15:48:53 -04:00
case 'wakeonlan' : {
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
2020-01-07 16:04:36 -05:00
sendConsoleText ( "Server requesting wake-on-lan for: " + data . macs . join ( ', ' ) ) ;
2017-09-20 17:44:22 -04:00
for ( var i in data . macs ) { sendWakeOnLan ( data . macs [ i ] ) ; }
2017-08-28 15:48:53 -04:00
break ;
}
2020-07-11 02:12:43 -04:00
case 'runcommands' : {
if ( mesh . cmdchild != null ) { sendConsoleText ( "Run commands can't execute, already busy." ) ; break ; }
2020-09-29 16:20:08 -04:00
sendConsoleText ( "Run commands (" + data . runAsUser + "): " + data . cmds ) ;
// data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
var options = { } ;
if ( data . runAsUser > 0 ) {
2020-09-30 19:59:34 -04:00
try { options . uid = require ( 'user-sessions' ) . consoleUid ( ) ; } catch ( e ) { }
2020-09-29 16:20:08 -04:00
options . type = require ( 'child_process' ) . SpawnTypes . TERM ;
}
if ( data . runAsUser == 2 ) {
if ( options . uid == null ) break ;
if ( ( ( require ( 'user-sessions' ) . minUid != null ) && ( options . uid < require ( 'user-sessions' ) . minUid ( ) ) ) ) break ; // This command can only run as user.
}
2020-07-11 02:12:43 -04:00
if ( process . platform == 'win32' ) {
if ( data . type == 1 ) {
// Windows command shell
2020-09-29 16:20:08 -04:00
mesh . cmdchild = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ 'cmd' ] , options ) ;
2020-07-11 02:12:43 -04:00
mesh . cmdchild . descriptorMetadata = 'UserCommandsShell' ;
mesh . cmdchild . stdout . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stderr . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stdin . write ( data . cmds + '\r\nexit\r\n' ) ;
mesh . cmdchild . on ( 'exit' , function ( ) { sendConsoleText ( "Run commands completed." ) ; delete mesh . cmdchild ; } ) ;
} else if ( data . type == 2 ) {
// Windows Powershell
2020-09-29 16:20:08 -04:00
mesh . cmdchild = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' , [ 'powershell' , '-noprofile' , '-nologo' , '-command' , '-' ] , options ) ;
2020-07-11 02:12:43 -04:00
mesh . cmdchild . descriptorMetadata = 'UserCommandsPowerShell' ;
mesh . cmdchild . stdout . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stderr . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stdin . write ( data . cmds + '\r\nexit\r\n' ) ;
mesh . cmdchild . on ( 'exit' , function ( ) { sendConsoleText ( "Run commands completed." ) ; delete mesh . cmdchild ; } ) ;
}
} else if ( data . type == 3 ) {
// Linux shell
2020-09-29 16:20:08 -04:00
mesh . cmdchild = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] , options ) ;
2020-07-11 02:12:43 -04:00
mesh . cmdchild . descriptorMetadata = 'UserCommandsShell' ;
mesh . cmdchild . stdout . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stderr . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stdin . write ( data . cmds . split ( '\r' ) . join ( '' ) + '\nexit\n' ) ;
mesh . cmdchild . on ( 'exit' , function ( ) { sendConsoleText ( "Run commands completed." ) ; delete mesh . cmdchild ; } ) ;
}
break ;
}
2019-10-24 16:13:18 -04:00
case 'uninstallagent' :
// Uninstall this agent
var agentName = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
if ( require ( 'service-manager' ) . manager . getService ( agentName ) . isMe ( ) ) {
2020-09-30 19:59:34 -04:00
try { diagnosticAgent _uninstall ( ) ; } catch ( e ) { }
2019-10-24 16:13:18 -04:00
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();" ;
this . child = require ( 'child_process' ) . execFile ( process . execPath , [ process . platform == 'win32' ? ( process . execPath . split ( '\\' ) . pop ( ) ) : ( process . execPath . split ( '/' ) . pop ( ) ) , '-b64exec' , Buffer . from ( js ) . toString ( 'base64' ) ] , { type : 4 , detached : true } ) ;
}
break ;
2017-09-01 14:23:22 -04:00
case 'poweraction' : {
// Server telling us to execute a power action
if ( ( mesh . ExecPowerState != undefined ) && ( data . actiontype ) ) {
var forced = 0 ;
if ( data . forced == 1 ) { forced = 1 ; }
data . actiontype = parseInt ( data . actiontype ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 25 , [ data . actiontype , forced ] , "Performing power action=" + data . actiontype + ", forced=" + forced , data ) ;
2020-01-07 16:04:36 -05:00
sendConsoleText ( "Performing power action=" + data . actiontype + ", forced=" + forced + '.' ) ;
2017-09-01 14:23:22 -04:00
var r = mesh . ExecPowerState ( data . actiontype , forced ) ;
2020-01-07 16:04:36 -05:00
sendConsoleText ( "ExecPowerState returned code: " + r ) ;
2017-09-01 14:23:22 -04:00
}
break ;
}
2017-09-05 20:19:28 -04:00
case 'iplocation' : {
// Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
2020-01-07 16:04:36 -05:00
getIpLocationData ( function ( location ) { mesh . SendCommand ( { action : 'iplocation' , type : 'publicip' , value : location } ) ; } ) ;
2017-08-28 15:48:53 -04:00
break ;
2017-08-28 12:27:45 -04:00
}
2018-04-19 21:19:15 -04:00
case 'toast' : {
// Display a toast message
2019-05-08 19:18:44 -04:00
if ( data . title && data . msg ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 26 , [ data . title , data . msg ] , "Displaying toast message, title=" + data . title + ", message=" + data . msg , data ) ;
2019-10-09 18:56:27 -04:00
data . msg = data . msg . split ( '\r' ) . join ( '\\r' ) . split ( '\n' ) . join ( '\\n' ) ;
2020-09-30 19:59:34 -04:00
try { require ( 'toaster' ) . Toast ( data . title , data . msg ) ; } catch ( e ) { }
2019-05-08 19:18:44 -04:00
}
2018-04-19 21:19:15 -04:00
break ;
}
2018-12-07 19:36:27 -05:00
case 'openUrl' : {
// Open a local web browser and return success/fail
2019-07-29 19:35:48 -04:00
//sendConsoleText('OpenURL: ' + data.url);
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 20 , [ data . url ] , "Opening: " + data . url , data ) ;
2020-01-07 16:04:36 -05:00
if ( data . url ) { mesh . SendCommand ( { action : 'openUrl' , url : data . url , sessionid : data . sessionid , success : ( openUserDesktopUrl ( data . url ) != null ) } ) ; }
2018-12-07 19:36:27 -05:00
break ;
}
2019-02-07 18:00:10 -05:00
case 'amtPolicy' : {
// Store the latest Intel AMT policy
amtPolicy = data . amtPolicy ;
if ( data . amtPolicy != null ) { db . Put ( 'amtPolicy' , JSON . stringify ( data . amtPolicy ) ) ; } else { db . Put ( 'amtPolicy' , null ) ; }
2019-06-20 19:56:19 -04:00
if ( amt != null ) { amt . setPolicy ( amtPolicy , true ) ; }
2019-02-07 18:00:10 -05:00
break ;
}
2019-02-07 23:06:01 -05:00
case 'getScript' : {
// Received a configuration script from the server
sendConsoleText ( 'getScript: ' + JSON . stringify ( data ) ) ;
break ;
}
2019-08-06 20:58:29 -04:00
case 'sysinfo' : {
// Fetch system information
2019-09-23 19:46:26 -04:00
getSystemInformation ( function ( results ) {
2020-01-07 16:04:36 -05:00
if ( ( results != null ) && ( data . hash != results . hash ) ) { mesh . SendCommand ( { action : 'sysinfo' , sessionid : this . sessionid , data : results } ) ; }
2019-09-23 19:46:26 -04:00
} ) ;
2019-08-06 20:58:29 -04:00
break ;
}
2019-05-28 13:58:46 -04:00
case 'ping' : { mesh . SendCommand ( '{"action":"pong"}' ) ; break ; }
case 'pong' : { break ; }
2019-10-10 14:13:25 -04:00
case 'plugin' : {
2019-10-24 16:41:35 -04:00
try { require ( data . plugin ) . consoleaction ( data , data . rights , data . sessionid , this ) ; } catch ( e ) { throw e ; }
2019-10-10 14:13:25 -04:00
break ;
}
2020-07-06 19:01:44 -04:00
case 'coredump' :
2020-07-08 02:56:08 -04:00
// Set the current agent coredump situation.
2020-07-06 19:01:44 -04:00
if ( data . value === true ) {
// TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
process . coreDumpLocation = ( process . platform == 'win32' ) ? ( process . execPath . replace ( '.exe' , '.dmp' ) ) : ( process . execPath + '.dmp' ) ;
} else if ( data . value === false ) {
process . coreDumpLocation = null ;
}
break ;
2020-07-06 20:16:54 -04:00
case 'getcoredump' :
2020-07-08 02:56:08 -04:00
// Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
2020-07-06 20:16:54 -04:00
var r = { action : 'getcoredump' , value : ( process . coreDumpLocation != null ) } ;
2020-07-08 02:56:08 -04:00
var coreDumpPath = null ;
if ( process . platform == 'win32' ) { coreDumpPath = process . coreDumpLocation ; } else { coreDumpPath = ( process . cwd ( ) != '//' ) ? fs . existsSync ( process . cwd ( ) + 'core' ) : null ; }
if ( ( coreDumpPath != null ) && ( fs . existsSync ( coreDumpPath ) ) ) { r . exists = ( db . Get ( 'CoreDumpTime' ) != require ( 'fs' ) . statSync ( coreDumpPath ) . mtime ) ; }
if ( r . exists == true ) { r . agenthashhex = getSHA384FileHash ( process . execPath ) . toString ( 'hex' ) ; }
2020-07-06 20:16:54 -04:00
mesh . SendCommand ( JSON . stringify ( r ) ) ;
2019-01-04 20:59:13 -05:00
default :
// Unknown action, ignore it.
break ;
2017-08-28 12:27:45 -04:00
}
}
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Called when a file changed in the file system
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04:00
function onFileWatcher ( a , b ) {
2017-10-23 17:09:58 -04:00
console . log ( 'onFileWatcher' , a , b , this . path ) ;
2017-08-28 12:27:45 -04:00
var response = getDirectoryInfo ( this . path ) ;
if ( ( response != undefined ) && ( response != null ) ) { this . tunnel . s . write ( JSON . stringify ( response ) ) ; }
}
2017-12-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04:00
2019-09-23 19:46:26 -04:00
function getSystemInformation ( func ) {
try {
var results = { hardware : require ( 'identifiers' ) . get ( ) } ; // Hardware info
if ( results . hardware && results . hardware . windows ) {
// Remove extra entries and things that change quickly
var x = results . hardware . windows . osinfo ;
2020-09-30 19:59:34 -04:00
try { delete x . FreePhysicalMemory ; } catch ( e ) { }
try { delete x . FreeSpaceInPagingFiles ; } catch ( e ) { }
try { delete x . FreeVirtualMemory ; } catch ( e ) { }
try { delete x . LocalDateTime ; } catch ( e ) { }
try { delete x . MaxProcessMemorySize ; } catch ( e ) { }
try { delete x . TotalVirtualMemorySize ; } catch ( e ) { }
try { delete x . TotalVisibleMemorySize ; } catch ( e ) { }
2019-09-23 19:46:26 -04:00
try {
if ( results . hardware . windows . memory ) { for ( var i in results . hardware . windows . memory ) { delete results . hardware . windows . memory [ i ] . Node ; } }
if ( results . hardware . windows . osinfo ) { delete results . hardware . windows . osinfo . Node ; }
if ( results . hardware . windows . partitions ) { for ( var i in results . hardware . windows . partitions ) { delete results . hardware . windows . partitions [ i ] . Node ; } }
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2019-09-23 19:46:26 -04:00
}
if ( process . platform == 'win32' ) { results . pendingReboot = require ( 'win-info' ) . pendingReboot ( ) ; } // Pending reboot
/ *
if ( process . platform == 'win32' ) {
var defragResult = function ( r ) {
if ( typeof r == 'object' ) { results [ this . callname ] = r ; }
if ( this . callname == 'defrag' ) {
var pr = require ( 'win-info' ) . installedApps ( ) ; // Installed apps
pr . callname = 'installedApps' ;
pr . sessionid = data . sessionid ;
pr . then ( defragResult , defragResult ) ;
}
else {
results . winpatches = require ( 'win-info' ) . qfe ( ) ; // Windows patches
results . hash = require ( 'SHA384Stream' ) . create ( ) . syncHash ( JSON . stringify ( results ) ) . toString ( 'hex' ) ;
func ( results ) ;
}
}
var pr = require ( 'win-info' ) . defrag ( { volume : 'C:' } ) ; // Defrag TODO
pr . callname = 'defrag' ;
pr . sessionid = data . sessionid ;
pr . then ( defragResult , defragResult ) ;
} else {
* /
2019-10-10 14:13:25 -04:00
results . hash = require ( 'SHA384Stream' ) . create ( ) . syncHash ( JSON . stringify ( results ) ) . toString ( 'hex' ) ;
func ( results ) ;
2019-09-23 19:46:26 -04:00
//}
2020-09-30 19:59:34 -04:00
} catch ( e ) { func ( null , e ) ; }
2019-09-23 19:46:26 -04:00
}
2017-08-28 12:27:45 -04:00
// Get a formated response for a given directory path
function getDirectoryInfo ( reqpath ) {
var response = { path : reqpath , dir : [ ] } ;
if ( ( ( reqpath == undefined ) || ( reqpath == '' ) ) && ( process . platform == 'win32' ) ) {
// List all the drives in the root, or the root itself
var results = null ;
try { results = fs . readDrivesSync ( ) ; } catch ( e ) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
if ( results != null ) {
for ( var i = 0 ; i < results . length ; ++ i ) {
var drive = { n : results [ i ] . name , t : 1 } ;
if ( results [ i ] . type == 'REMOVABLE' ) { drive . dt = 'removable' ; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
response . dir . push ( drive ) ;
}
}
} else {
// List all the files and folders in this path
if ( reqpath == '' ) { reqpath = '/' ; }
2018-07-06 13:13:19 -04:00
var results = null , xpath = obj . path . join ( reqpath , '*' ) ;
2018-07-26 19:31:43 -04:00
//if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
2017-08-28 12:27:45 -04:00
try { results = fs . readdirSync ( xpath ) ; } catch ( e ) { }
if ( results != null ) {
for ( var i = 0 ; i < results . length ; ++ i ) {
if ( ( results [ i ] != '.' ) && ( results [ i ] != '..' ) ) {
var stat = null , p = obj . path . join ( reqpath , results [ i ] ) ;
2018-07-26 19:31:43 -04:00
//if (process.platform == "win32") { p = p.split('/').join('\\'); }
2017-08-28 12:27:45 -04:00
try { stat = fs . statSync ( p ) ; } catch ( e ) { } // TODO: Get file size/date
if ( ( stat != null ) && ( stat != undefined ) ) {
if ( stat . isDirectory ( ) == true ) {
response . dir . push ( { n : results [ i ] , t : 2 , d : stat . mtime } ) ;
} else {
response . dir . push ( { n : results [ i ] , t : 3 , s : stat . size , d : stat . mtime } ) ;
}
}
}
}
}
}
return response ;
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Tunnel callback operations
2017-10-23 17:09:58 -04:00
function onTunnelUpgrade ( response , s , head ) {
this . s = s ;
s . httprequest = this ;
s . end = onTunnelClosed ;
2018-01-18 18:43:43 -05:00
s . tunnel = this ;
2020-05-21 16:22:58 -04:00
s . descriptorMetadata = "MeshAgent_relayTunnel" ;
2019-01-04 17:35:01 -05:00
2020-02-01 00:35:47 -05:00
if ( require ( 'MeshAgent' ) . idleTimeout != null )
{
s . setTimeout ( require ( 'MeshAgent' ) . idleTimeout * 1000 ) ;
s . on ( 'timeout' , function ( )
{
this . ping ( ) ;
this . setTimeout ( require ( 'MeshAgent' ) . idleTimeout * 1000 ) ;
} ) ;
}
2019-05-06 21:44:23 -04:00
//sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
2019-01-04 17:35:01 -05:00
2017-10-23 17:09:58 -04:00
if ( this . tcpport != null ) {
// This is a TCP relay connection, pause now and try to connect to the target.
s . pause ( ) ;
s . data = onTcpRelayServerTunnelData ;
var connectionOptions = { port : parseInt ( this . tcpport ) } ;
if ( this . tcpaddr != null ) { connectionOptions . host = this . tcpaddr ; } else { connectionOptions . host = '127.0.0.1' ; }
s . tcprelay = net . createConnection ( connectionOptions , onTcpRelayTargetTunnelConnect ) ;
s . tcprelay . peerindex = this . index ;
2020-07-24 17:00:49 -04:00
// Add the TCP session to the count and update the server
if ( s . httprequest . userid != null ) {
if ( tunnelUserCount . tcp [ s . httprequest . userid ] == null ) { tunnelUserCount . tcp [ s . httprequest . userid ] = 1 ; } else { tunnelUserCount . tcp [ s . httprequest . userid ] ++ ; }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'tcp' , value : tunnelUserCount . tcp } ) ; } catch ( e ) { }
2020-07-24 17:00:49 -04:00
}
2019-05-05 01:53:57 -04:00
} if ( this . udpport != null ) {
// This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
s . data = onUdpRelayServerTunnelData ;
2019-05-06 21:44:23 -04:00
s . udprelay = require ( 'dgram' ) . createSocket ( { type : 'udp4' } ) ;
2019-05-05 01:53:57 -04:00
s . udprelay . bind ( { port : 0 } ) ;
s . udprelay . peerindex = this . index ;
s . udprelay . on ( 'message' , onUdpRelayTargetTunnelConnect ) ;
2019-05-06 21:44:23 -04:00
s . udprelay . udpport = this . udpport ;
s . udprelay . udpaddr = this . udpaddr ;
s . udprelay . first = true ;
2020-07-24 17:00:49 -04:00
// Add the UDP session to the count and update the server
if ( s . httprequest . userid != null ) {
if ( tunnelUserCount . udp [ s . httprequest . userid ] == null ) { tunnelUserCount . udp [ s . httprequest . userid ] = 1 ; } else { tunnelUserCount . udp [ s . httprequest . userid ] ++ ; }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'udp' , value : tunnelUserCount . tcp } ) ; } catch ( e ) { }
2020-07-24 17:00:49 -04:00
}
2017-10-23 17:09:58 -04:00
} else {
// This is a normal connect for KVM/Terminal/Files
s . data = onTunnelData ;
}
}
2019-05-05 01:53:57 -04:00
// Called when UDP relay data is received // TODO****
function onUdpRelayTargetTunnelConnect ( data ) {
var peerTunnel = tunnels [ this . peerindex ] ;
2019-05-07 15:39:49 -04:00
peerTunnel . s . write ( data ) ;
2019-05-05 01:53:57 -04:00
}
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onUdpRelayServerTunnelData ( data ) {
2019-05-06 21:44:23 -04:00
if ( this . udprelay . first === true ) {
delete this . udprelay . first ; // Skip the first 'c' that is received.
} else {
this . udprelay . send ( data , parseInt ( this . udprelay . udpport ) , this . udprelay . udpaddr ? this . udprelay . udpaddr : '127.0.0.1' ) ;
}
2019-05-05 01:53:57 -04:00
}
2017-10-23 17:09:58 -04:00
// Called when the TCP relay target is connected
function onTcpRelayTargetTunnelConnect ( ) {
var peerTunnel = tunnels [ this . peerindex ] ;
this . pipe ( peerTunnel . s ) ; // Pipe Target --> Server
peerTunnel . s . first = true ;
peerTunnel . s . resume ( ) ;
}
2019-10-10 14:13:25 -04:00
2017-10-23 17:09:58 -04:00
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onTcpRelayServerTunnelData ( data ) {
2020-07-19 17:45:29 -04:00
if ( this . first == true ) {
this . first = false ;
this . pipe ( this . tcprelay , { dataTypeSkip : 1 } ) ; // Pipe Server --> Target (don't pipe text type websocket frames)
}
2017-10-23 17:09:58 -04:00
}
2019-05-05 01:53:57 -04:00
2017-08-28 12:27:45 -04:00
function onTunnelClosed ( ) {
2020-07-17 22:55:49 -04:00
var tunnel = tunnels [ this . httprequest . index ] ;
if ( tunnel == null ) return ; // Stop duplicate calls.
2020-07-24 17:00:49 -04:00
// If this is a routing session, clean up and send the new session counts.
if ( this . httprequest . userid != null ) {
if ( this . httprequest . tcpport != null ) {
if ( tunnelUserCount . tcp [ this . httprequest . userid ] != null ) { tunnelUserCount . tcp [ this . httprequest . userid ] -- ; if ( tunnelUserCount . tcp [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . tcp [ this . httprequest . userid ] ; } }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'tcp' , value : tunnelUserCount . tcp } ) ; } catch ( e ) { }
2020-07-24 17:00:49 -04:00
} else if ( this . httprequest . udpport != null ) {
if ( tunnelUserCount . udp [ this . httprequest . userid ] != null ) { tunnelUserCount . udp [ this . httprequest . userid ] -- ; if ( tunnelUserCount . udp [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . udp [ this . httprequest . userid ] ; } }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'udp' , value : tunnelUserCount . udp } ) ; } catch ( e ) { }
2020-07-24 17:00:49 -04:00
}
}
2020-07-21 19:20:17 -04:00
// Sent tunnel statistics to the server, only send this if compression was used.
2020-08-26 21:42:41 -04:00
if ( ( this . bytesSent _uncompressed ) && ( this . bytesSent _uncompressed . toString ( ) != this . bytesSent _actual . toString ( ) ) ) {
2020-07-21 19:20:17 -04:00
mesh . SendCommand ( {
action : 'tunnelCloseStats' ,
url : tunnel . url ,
userid : tunnel . userid ,
protocol : tunnel . protocol ,
sessionid : tunnel . sessionid ,
sent : this . bytesSent _uncompressed . toString ( ) ,
sentActual : this . bytesSent _actual . toString ( ) ,
sentRatio : this . bytesSent _ratio ,
received : this . bytesReceived _uncompressed . toString ( ) ,
receivedActual : this . bytesReceived _actual . toString ( ) ,
receivedRatio : this . bytesReceived _ratio
} ) ;
}
2020-07-17 02:08:59 -04:00
2020-07-17 22:55:49 -04:00
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
2020-08-26 21:42:41 -04:00
if ( this . httprequest . index ) { delete tunnels [ this . httprequest . index ] ; }
2019-10-10 14:13:25 -04:00
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04:00
// Close the watcher if required
if ( this . httprequest . watcher != undefined ) {
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2017-10-23 17:09:58 -04:00
delete this . httprequest . watcher ;
2017-08-28 12:27:45 -04:00
}
2017-12-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04:00
// If there is a upload or download active on this connection, close the file
2020-08-13 15:29:18 -04:00
if ( this . httprequest . uploadFile ) { fs . closeSync ( this . httprequest . uploadFile ) ; delete this . httprequest . uploadFile ; delete this . httprequest . uploadFileid ; delete this . httprequest . uploadFilePath ; }
2020-08-26 21:42:41 -04:00
if ( this . httprequest . downloadFile ) { delete this . httprequest . downloadFile ; }
2018-01-18 18:43:43 -05:00
// Clean up WebRTC
if ( this . webrtc != null ) {
if ( this . webrtc . rtcchannel ) { try { this . webrtc . rtcchannel . close ( ) ; } catch ( e ) { } this . webrtc . rtcchannel . removeAllListeners ( 'data' ) ; this . webrtc . rtcchannel . removeAllListeners ( 'end' ) ; delete this . webrtc . rtcchannel ; }
if ( this . webrtc . websocket ) { delete this . webrtc . websocket ; }
try { this . webrtc . close ( ) ; } catch ( e ) { }
this . webrtc . removeAllListeners ( 'connected' ) ;
this . webrtc . removeAllListeners ( 'disconnected' ) ;
this . webrtc . removeAllListeners ( 'dataChannel' ) ;
delete this . webrtc ;
}
// Clean up WebSocket
this . removeAllListeners ( 'data' ) ;
2017-08-28 12:27:45 -04:00
}
2018-11-27 20:13:01 -05:00
function onTunnelSendOk ( ) { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
2017-08-28 12:27:45 -04:00
function onTunnelData ( data ) {
2017-12-19 11:50:19 -05:00
//console.log("OnTunnelData");
2018-07-06 13:13:19 -04:00
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// If this is upload data, save it to file
2020-08-13 15:29:18 -04:00
if ( ( this . httprequest . uploadFile ) && ( typeof data == 'object' ) && ( data [ 0 ] != 123 ) ) {
// Save the data to file being uploaded.
if ( data [ 0 ] == 0 ) {
// If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
try { fs . writeSync ( this . httprequest . uploadFile , data , 1 , data . length - 1 ) ; } catch ( e ) { sendConsoleText ( 'FileUpload Error' ) ; this . write ( Buffer . from ( JSON . stringify ( { action : 'uploaderror' } ) ) ) ; return ; } // Write to the file, if there is a problem, error out.
} else {
// If data does not start with zero, save as-is.
try { fs . writeSync ( this . httprequest . uploadFile , data ) ; } catch ( e ) { sendConsoleText ( 'FileUpload Error' ) ; this . write ( Buffer . from ( JSON . stringify ( { action : 'uploaderror' } ) ) ) ; return ; } // Write to the file, if there is a problem, error out.
2020-04-20 23:12:35 -04:00
}
2020-08-13 15:29:18 -04:00
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadack' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Ask for more data.
2017-08-28 12:27:45 -04:00
return ;
}
2018-02-11 20:13:26 -05:00
2017-08-28 12:27:45 -04:00
if ( this . httprequest . state == 0 ) {
// Check if this is a relay connection
2019-08-13 14:49:05 -04:00
if ( ( data == 'c' ) || ( data == 'cr' ) ) { this . httprequest . state = 1 ; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
2020-05-17 00:10:11 -04:00
}
else
{
2017-08-28 12:27:45 -04:00
// Handle tunnel data
2020-08-26 21:42:41 -04:00
if ( this . httprequest . protocol == 0 ) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
2018-02-11 20:13:26 -05:00
// Take a look at the protocol
2019-12-12 20:45:42 -05:00
if ( ( data . length > 3 ) && ( data [ 0 ] == '{' ) ) { onTunnelControlData ( data , this ) ; return ; }
2017-08-28 12:27:45 -04:00
this . httprequest . protocol = parseInt ( data ) ;
if ( typeof this . httprequest . protocol != 'number' ) { this . httprequest . protocol = 0 ; }
2020-08-26 21:42:41 -04:00
if ( this . httprequest . protocol == 10 ) {
//
// Basic file transfer
//
var stats = null ;
2020-08-28 21:29:32 -04:00
if ( ( process . platform != 'win32' ) && ( this . httprequest . xoptions . file . startsWith ( '/' ) == false ) ) { this . httprequest . xoptions . file = '/' + this . httprequest . xoptions . file ; }
2020-08-26 21:42:41 -04:00
try { stats = require ( 'fs' ) . statSync ( this . httprequest . xoptions . file ) } catch ( e ) { }
try { if ( stats ) { this . httprequest . downloadFile = fs . createReadStream ( this . httprequest . xoptions . file , { flags : 'rbN' } ) ; } } catch ( e ) { }
if ( this . httprequest . downloadFile ) {
2020-08-28 23:01:06 -04:00
//sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
2020-08-26 21:42:41 -04:00
this . write ( JSON . stringify ( { op : 'ok' , size : stats . size } ) ) ;
this . httprequest . downloadFile . pipe ( this ) ;
this . httprequest . downloadFile . end = function ( ) { }
} else {
2020-08-28 23:01:06 -04:00
//sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
2020-08-26 21:42:41 -04:00
this . write ( JSON . stringify ( { op : 'cancel' } ) ) ;
}
}
else if ( ( this . httprequest . protocol == 1 ) || ( this . httprequest . protocol == 6 ) || ( this . httprequest . protocol == 8 ) || ( this . httprequest . protocol == 9 ) )
2020-05-17 00:10:11 -04:00
{
//
// Remote Terminal
//
2019-03-01 14:47:58 -05:00
// Check user access rights for terminal
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NOTERMINAL ) != 0 ) ) ) {
2018-11-27 20:13:01 -05:00
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
2019-03-01 14:47:58 -05:00
this . httprequest . s . end ( ) ;
2020-01-07 16:04:36 -05:00
sendConsoleText ( "Error: No Terminal Control Rights." ) ;
2018-11-27 20:13:01 -05:00
return ;
}
2020-05-21 16:22:58 -04:00
this . descriptorMetadata = "Remote Terminal" ;
2020-05-17 00:10:11 -04:00
if ( process . platform == 'win32' )
{
if ( ! require ( 'win-terminal' ) . PowerShellCapable ( ) && ( this . httprequest . protocol == 6 || this . httprequest . protocol == 9 ) )
{
this . httprequest . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : 'PowerShell is not supported on this version of windows' , msgid : 1 } ) ) ;
this . httprequest . s . end ( ) ;
return ;
}
}
2020-04-15 22:05:46 -04:00
var prom = require ( 'promise' ) ;
this . httprequest . tpromise = new prom ( function ( res , rej ) { this . _res = res ; this . _rej = rej ; } ) ;
2020-05-17 00:10:11 -04:00
this . httprequest . tpromise . that = this ;
this . httprequest . tpromise . httprequest = this . httprequest ;
2020-04-15 22:05:46 -04:00
2020-05-17 00:10:11 -04:00
this . end = function ( )
{
if ( this . httprequest . tpromise . _consent ) { this . httprequest . tpromise . _consent . close ( ) ; }
2020-05-19 04:21:07 -04:00
if ( this . httprequest . connectionPromise ) { this . httprequest . connectionPromise . _rej ( 'Closed' ) ; }
2020-05-12 03:01:42 -04:00
// Remove the terminal session to the count to update the server
2020-05-17 00:10:11 -04:00
if ( this . httprequest . userid != null )
{
2020-05-12 03:01:42 -04:00
if ( tunnelUserCount . terminal [ this . httprequest . userid ] != null ) { tunnelUserCount . terminal [ this . httprequest . userid ] -- ; if ( tunnelUserCount . terminal [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . terminal [ this . httprequest . userid ] ; } }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'terminal' , value : tunnelUserCount . terminal } ) ; } catch ( e ) { }
2020-05-12 03:01:42 -04:00
}
2020-05-17 00:10:11 -04:00
if ( process . platform == 'win32' )
{
2019-06-14 19:33:53 -04:00
// Unpipe the web socket
this . unpipe ( this . httprequest . _term ) ;
2019-08-14 01:48:06 -04:00
if ( this . httprequest . _term ) { this . httprequest . _term . unpipe ( this ) ; }
2019-06-14 19:33:53 -04:00
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
2020-05-17 00:10:11 -04:00
if ( this . rtcchannel )
{
2019-06-14 19:33:53 -04:00
this . rtcchannel . unpipe ( this . httprequest . _term ) ;
2019-08-14 01:48:06 -04:00
if ( this . httprequest . _term ) { this . httprequest . _term . unpipe ( this . rtcchannel ) ; }
2019-06-14 19:33:53 -04:00
}
2019-05-20 19:00:33 -04:00
2019-06-14 19:33:53 -04:00
// Clean up
2019-08-14 01:48:06 -04:00
if ( this . httprequest . _term ) { this . httprequest . _term . end ( ) ; }
2019-06-14 19:33:53 -04:00
this . httprequest . _term = null ;
}
} ;
2019-04-12 17:19:03 -04:00
2020-05-17 00:10:11 -04:00
// Perform User-Consent if needed.
if ( this . httprequest . consent && ( this . httprequest . consent & 16 ) )
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
this . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : "Waiting for user to grant access..." , msgid : 1 } ) ) ;
2020-06-13 14:36:50 -04:00
var consentMessage = this . httprequest . username + " requesting remote terminal access. Grant access?" , consentTitle = 'MeshCentral' ;
if ( this . httprequest . soptions != null ) {
if ( this . httprequest . soptions . consentTitle != null ) { consentTitle = this . httprequest . soptions . consentTitle ; }
2020-07-21 19:20:17 -04:00
if ( this . httprequest . soptions . consentMsgTerminal != null ) { consentMessage = this . httprequest . soptions . consentMsgTerminal . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
this . httprequest . tpromise . _consent = require ( 'message-box' ) . create ( consentTitle , consentMessage , 30 ) ;
2020-05-17 00:10:11 -04:00
this . httprequest . tpromise . _consent . retPromise = this . httprequest . tpromise ;
this . httprequest . tpromise . _consent . then (
function ( )
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
// Success
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 27 , null , "Local user accepted remote terminal request (" + this . retPromise . httprequest . remoteaddr + ")" , this . retPromise . that . httprequest ) ;
2020-05-17 00:10:11 -04:00
this . retPromise . that . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : null , msgid : 0 } ) ) ;
this . retPromise . _consent = null ;
this . retPromise . _res ( ) ;
} ,
function ( e )
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
// Denied
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 28 , null , "Local user rejected remote terminal request (" + this . retPromise . that . httprequest . remoteaddr + ")" , this . retPromise . that . httprequest ) ;
2020-05-17 00:10:11 -04:00
this . retPromise . that . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
this . retPromise . _rej ( e . toString ( ) ) ;
} ) ;
}
else
{
// User-Consent is not required, so just resolve this promise
this . httprequest . tpromise . _res ( ) ;
}
2020-01-28 18:08:51 -05:00
2020-05-17 00:10:11 -04:00
this . httprequest . tpromise . then (
function ( )
{
this . httprequest . connectionPromise = new prom ( function ( res , rej ) { this . _res = res ; this . _rej = rej ; } ) ;
this . httprequest . connectionPromise . ws = this . that ;
// Start Terminal
if ( process . platform == 'win32' )
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
try
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
var cols = 80 , rows = 25 ;
if ( this . httprequest . xoptions )
{
if ( this . httprequest . xoptions . rows ) { rows = this . httprequest . xoptions . rows ; }
if ( this . httprequest . xoptions . cols ) { cols = this . httprequest . xoptions . cols ; }
}
if ( ( this . httprequest . protocol == 1 ) || ( this . httprequest . protocol == 6 ) )
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
// Admin Terminal
2020-01-28 18:08:51 -05:00
if ( require ( 'win-virtual-terminal' ) . supported )
{
2019-12-16 18:07:20 -05:00
// ConPTY PseudoTerminal
2020-05-17 00:10:11 -04:00
// this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
// The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
this . httprequest . _dispatcher = require ( 'win-dispatcher' ) . dispatch ( { modules : [ { name : 'win-virtual-terminal' , script : getJSModule ( 'win-virtual-terminal' ) } ] , launch : { module : 'win-virtual-terminal' , method : ( this . httprequest . protocol == 6 ? 'StartPowerShell' : 'Start' ) , args : [ cols , rows ] } } ) ;
this . httprequest . _dispatcher . httprequest = this . httprequest ;
this . httprequest . _dispatcher . on ( 'connection' , function ( c )
{
2020-05-19 04:21:07 -04:00
if ( this . httprequest . connectionPromise . completed )
{
c . end ( ) ;
}
else
{
this . httprequest . connectionPromise . _res ( c ) ;
}
2020-05-17 00:10:11 -04:00
} ) ;
2020-01-28 18:08:51 -05:00
}
else
{
2019-12-16 18:07:20 -05:00
// Legacy Terminal
2020-05-17 00:10:11 -04:00
this . httprequest . connectionPromise . _res ( require ( 'win-terminal' ) [ this . httprequest . protocol == 6 ? 'StartPowerShell' : 'Start' ] ( cols , rows ) ) ;
2019-12-16 18:07:20 -05:00
}
2020-05-17 00:10:11 -04:00
}
else
{
// Logged in user
var userPromise = require ( 'user-sessions' ) . enumerateUsers ( ) ;
userPromise . that = this ;
userPromise . then ( function ( u )
2020-01-28 18:08:51 -05:00
{
2020-05-17 00:10:11 -04:00
var that = this . that ;
if ( u . Active . length > 0 )
{
var username = u . Active [ 0 ] . Username ;
if ( require ( 'win-virtual-terminal' ) . supported )
{
// ConPTY PseudoTerminal
that . httprequest . _dispatcher = require ( 'win-dispatcher' ) . dispatch ( { user : username , modules : [ { name : 'win-virtual-terminal' , script : getJSModule ( 'win-virtual-terminal' ) } ] , launch : { module : 'win-virtual-terminal' , method : ( that . httprequest . protocol == 9 ? 'StartPowerShell' : 'Start' ) , args : [ cols , rows ] } } ) ;
}
else
{
// Legacy Terminal
that . httprequest . _dispatcher = require ( 'win-dispatcher' ) . dispatch ( { user : username , modules : [ { name : 'win-terminal' , script : getJSModule ( 'win-terminal' ) } ] , launch : { module : 'win-terminal' , method : ( that . httprequest . protocol == 9 ? 'StartPowerShell' : 'Start' ) , args : [ cols , rows ] } } ) ;
}
that . httprequest . _dispatcher . ws = that ;
that . httprequest . _dispatcher . on ( 'connection' , function ( c )
{
2020-05-19 04:21:07 -04:00
if ( this . ws . httprequest . connectionPromise . completed )
{
c . end ( ) ;
}
else
{
this . ws . httprequest . connectionPromise . _res ( c ) ;
}
2020-05-17 00:10:11 -04:00
} ) ;
}
2019-12-16 18:07:20 -05:00
} ) ;
}
2020-05-17 00:10:11 -04:00
}
catch ( e )
{
this . httprequest . connectionPromise . _rej ( 'Failed to start remote terminal session, ' + e . toString ( ) ) ;
}
2020-01-28 18:08:51 -05:00
}
else
{
2020-05-17 00:10:11 -04:00
try
{
var bash = fs . existsSync ( '/bin/bash' ) ? '/bin/bash' : false ;
var sh = fs . existsSync ( '/bin/sh' ) ? '/bin/sh' : false ;
var login = process . platform == 'linux' ? '/bin/login' : '/usr/bin/login' ;
var env = { HISTCONTROL : 'ignoreboth' } ;
if ( this . httprequest . xoptions )
{
if ( this . httprequest . xoptions . rows ) { env . LINES = ( '' + this . httprequest . xoptions . rows ) ; }
if ( this . httprequest . xoptions . cols ) { env . COLUMNS = ( '' + this . httprequest . xoptions . cols ) ; }
}
var options = { type : childProcess . SpawnTypes . TERM , uid : ( this . httprequest . protocol == 8 ) ? require ( 'user-sessions' ) . consoleUid ( ) : null , env : env } ;
if ( this . httprequest . xoptions && this . httprequest . xoptions . requireLogin )
{
if ( ! require ( 'fs' ) . existsSync ( login ) ) { throw ( 'Unable to spawn login process' ) ; }
this . httprequest . connectionPromise . _res ( childProcess . execFile ( login , [ 'login' ] , options ) ) ; // Start login shell
}
else if ( bash )
{
var p = childProcess . execFile ( bash , [ 'bash' ] , options ) ; // Start bash
// Spaces at the beginning of lines are needed to hide commands from the command history
if ( process . platform == 'linux' ) { p . stdin . write ( ' alias ls=\'ls --color=auto\';clear\n' ) ; }
this . httprequest . connectionPromise . _res ( p ) ;
}
else if ( sh )
{
var p = childProcess . execFile ( sh , [ 'sh' ] , options ) ; // Start sh
// Spaces at the beginning of lines are needed to hide commands from the command history
if ( process . platform == 'linux' ) { p . stdin . write ( ' alias ls=\'ls --color=auto\';clear\n' ) ; }
this . httprequest . connectionPromise . _res ( p ) ;
}
else
{
this . httprequest . connectionPromise . _rej ( 'Failed to start remote terminal session, no shell found' ) ;
}
}
catch ( e )
{
this . httprequest . connectionPromise . _rej ( 'Failed to start remote terminal session, ' + e . toString ( ) ) ;
}
2019-11-25 19:11:53 -05:00
}
2020-01-28 18:08:51 -05:00
2020-05-17 00:10:11 -04:00
this . httprequest . connectionPromise . then (
function ( term )
{
// SUCCESS
var stdoutstream ;
var stdinstream ;
if ( process . platform == 'win32' )
{
this . ws . httprequest . _term = term ;
this . ws . httprequest . _term . tunnel = this . ws ;
stdoutstream = stdinstream = term ;
}
else
{
2020-05-22 15:23:12 -04:00
term . descriptorMetadata = 'Remote Terminal' ;
2020-05-17 00:10:11 -04:00
this . ws . httprequest . process = term ;
this . ws . httprequest . process . tunnel = this . ws ;
term . stderr . stdout = term . stdout ;
term . stderr . on ( 'data' , function ( c ) { this . stdout . write ( c ) ; } ) ;
stdoutstream = term . stdout ;
stdinstream = term . stdin ;
this . ws . prependListener ( 'end' , function ( ) { this . httprequest . process . kill ( ) ; } ) ;
term . prependListener ( 'exit' , function ( ) { this . tunnel . end ( ) ; } ) ;
}
2018-11-27 20:13:01 -05:00
2020-05-17 00:10:11 -04:00
this . ws . removeAllListeners ( 'data' ) ;
this . ws . on ( 'data' , onTunnelControlData ) ;
2020-05-12 03:01:42 -04:00
2020-05-17 00:10:11 -04:00
stdoutstream . pipe ( this . ws , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
this . ws . pipe ( stdinstream , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
2019-06-14 19:33:53 -04:00
2020-05-17 00:10:11 -04:00
// Add the terminal session to the count to update the server
if ( this . ws . httprequest . userid != null )
{
if ( tunnelUserCount . terminal [ this . ws . httprequest . userid ] == null ) { tunnelUserCount . terminal [ this . ws . httprequest . userid ] = 1 ; } else { tunnelUserCount . terminal [ this . ws . httprequest . userid ] ++ ; }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'terminal' , value : tunnelUserCount . terminal } ) ; } catch ( e ) { }
2020-05-17 00:10:11 -04:00
}
// Toast Notification, if required
2020-04-15 22:05:46 -04:00
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 2 ) )
{
// User Notifications is required
2020-06-13 14:36:50 -04:00
var notifyMessage = this . ws . httprequest . username + " started a remote terminal session." , notifyTitle = "MeshCentral" ;
if ( this . ws . httprequest . soptions != null ) {
if ( this . ws . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . ws . httprequest . soptions . notifyTitle ; }
2020-07-21 19:20:17 -04:00
if ( this . ws . httprequest . soptions . notifyMsgTerminal != null ) { notifyMessage = this . ws . httprequest . soptions . notifyMsgTerminal . replace ( '{0}' , this . ws . httprequest . realname ) . replace ( '{1}' , this . ws . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
2020-09-30 19:59:34 -04:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage ) ; } catch ( e ) { }
2020-04-15 22:05:46 -04:00
}
} ,
function ( e )
{
2020-05-17 00:10:11 -04:00
// FAILED to connect terminal
2020-05-03 15:54:51 -04:00
this . ws . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
2020-04-15 22:05:46 -04:00
this . ws . end ( ) ;
} ) ;
2020-05-17 00:10:11 -04:00
} ,
function ( e )
2020-04-15 22:05:46 -04:00
{
2020-05-17 00:10:11 -04:00
// DO NOT start terminal
this . that . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
this . that . end ( ) ;
} ) ;
}
else if ( this . httprequest . protocol == 2 )
{
//
// Remote KVM
//
2019-06-14 19:33:53 -04:00
2019-03-01 14:47:58 -05:00
// Check user access rights for desktop
2020-03-06 17:06:33 -05:00
if ( ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) && ( ( this . httprequest . rights & MESHRIGHT _REMOTEVIEW ) == 0 ) ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NODESKTOP ) != 0 ) ) ) {
2018-11-27 20:13:01 -05:00
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
2019-03-01 14:47:58 -05:00
this . httprequest . s . end ( ) ;
2020-01-07 16:04:36 -05:00
sendConsoleText ( "Error: No Desktop Control Rights." ) ;
2018-11-27 20:13:01 -05:00
return ;
}
2020-05-21 16:22:58 -04:00
this . descriptorMetadata = "Remote KVM" ;
2019-12-12 20:45:42 -05:00
// Look for a TSID
var tsid = null ;
if ( ( this . httprequest . xoptions != null ) && ( typeof this . httprequest . xoptions . tsid == 'number' ) ) { tsid = this . httprequest . xoptions . tsid ; }
2019-12-16 18:07:20 -05:00
require ( 'MeshAgent' ) . _tsid = tsid ;
2019-12-12 20:45:42 -05:00
2018-01-16 20:30:34 -05:00
// Remote desktop using native pipes
2019-12-12 20:45:42 -05:00
this . httprequest . desktop = { state : 0 , kvm : mesh . getRemoteDesktopStream ( tsid ) , tunnel : this } ;
2018-01-16 20:30:34 -05:00
this . httprequest . desktop . kvm . parent = this . httprequest . desktop ;
this . desktop = this . httprequest . desktop ;
2018-04-19 21:19:15 -04:00
2020-05-07 02:23:27 -04:00
// Add ourself to the list of remote desktop sessions
if ( this . httprequest . desktop . kvm . tunnels == null ) { this . httprequest . desktop . kvm . tunnels = [ ] ; }
this . httprequest . desktop . kvm . tunnels . push ( this ) ;
// Send a metadata update to all desktop sessions
var users = { } ;
2020-05-11 04:17:18 -04:00
if ( this . httprequest . desktop . kvm . tunnels != null ) {
2020-09-30 19:59:34 -04:00
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { var userid = this . httprequest . desktop . kvm . tunnels [ i ] . httprequest . userid ; if ( users [ userid ] == null ) { users [ userid ] = 1 ; } else { users [ userid ] ++ ; } } catch ( e ) { } }
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { this . httprequest . desktop . kvm . tunnels [ i ] . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'metadata' , users : users } ) ) ; } catch ( e ) { } }
try { mesh . SendCommand ( { action : 'sessions' , type : 'kvm' , value : users } ) ; } catch ( e ) { }
2020-05-11 04:17:18 -04:00
}
2020-05-07 02:23:27 -04:00
2020-01-07 16:04:36 -05:00
this . end = function ( ) {
2018-01-16 20:30:34 -05:00
-- this . desktop . kvm . connectionCount ;
2019-01-03 17:46:52 -05:00
2020-05-07 02:23:27 -04:00
// Remove ourself from the list of remote desktop session
var i = this . desktop . kvm . tunnels . indexOf ( this ) ;
if ( i >= 0 ) { this . desktop . kvm . tunnels . splice ( i , 1 ) ; }
// Send a metadata update to all desktop sessions
var users = { } ;
2020-05-11 04:17:18 -04:00
if ( this . httprequest . desktop . kvm . tunnels != null ) {
2020-09-30 19:59:34 -04:00
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { var userid = this . httprequest . desktop . kvm . tunnels [ i ] . httprequest . userid ; if ( users [ userid ] == null ) { users [ userid ] = 1 ; } else { users [ userid ] ++ ; } } catch ( e ) { } }
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { this . httprequest . desktop . kvm . tunnels [ i ] . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'metadata' , users : users } ) ) ; } catch ( e ) { } }
try { mesh . SendCommand ( { action : 'sessions' , type : 'kvm' , value : users } ) ; } catch ( e ) { }
2020-05-11 04:17:18 -04:00
}
2020-05-07 02:23:27 -04:00
2019-01-03 17:46:52 -05:00
// Unpipe the web socket
2020-04-28 14:36:04 -04:00
try
{
this . unpipe ( this . httprequest . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this ) ;
}
2020-09-30 19:59:34 -04:00
catch ( e ) { }
2019-01-03 17:46:52 -05:00
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
2020-04-28 14:36:04 -04:00
if ( this . rtcchannel )
{
try
{
this . rtcchannel . unpipe ( this . httprequest . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this . rtcchannel ) ;
}
2020-09-30 19:59:34 -04:00
catch ( e ) { }
2019-01-03 17:46:52 -05:00
}
2019-11-29 01:45:21 -05:00
// Place wallpaper back if needed
// TODO
2020-01-07 16:04:36 -05:00
if ( this . desktop . kvm . connectionCount == 0 ) {
2019-05-08 19:18:44 -04:00
// Display a toast message. This may not be supported on all platforms.
2020-09-30 19:59:34 -04:00
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
2019-10-10 14:13:25 -04:00
2018-04-19 21:19:15 -04:00
this . httprequest . desktop . kvm . end ( ) ;
2020-01-07 16:04:36 -05:00
if ( this . httprequest . desktop . kvm . connectionBar ) {
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . httprequest . desktop . kvm . connectionBar . close ( ) ;
this . httprequest . desktop . kvm . connectionBar = null ;
}
2020-05-07 02:23:27 -04:00
} else {
2020-01-07 16:04:36 -05:00
for ( var i in this . httprequest . desktop . kvm . users ) {
2020-07-13 14:30:27 -04:00
if ( ( this . httprequest . desktop . kvm . users [ i ] == this . httprequest . username ) && this . httprequest . desktop . kvm . connectionBar ) {
for ( var j in this . httprequest . desktop . kvm . rusers ) { if ( this . httprequest . desktop . kvm . rusers [ j ] == this . httprequest . realname ) { this . httprequest . desktop . kvm . rusers . splice ( j , 1 ) ; break ; } }
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . users . splice ( i , 1 ) ;
this . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . httprequest . desktop . kvm . connectionBar . close ( ) ;
2020-07-13 14:30:27 -04:00
this . httprequest . desktop . kvm . connectionBar = require ( 'notifybar-desktop' ) ( this . httprequest . privacybartext . replace ( '{0}' , this . httprequest . desktop . kvm . users . join ( ', ' ) ) . replace ( '{1}' , this . httprequest . desktop . kvm . rusers . join ( ', ' ) ) , require ( 'MeshAgent' ) . _tsid ) ;
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . connectionBar . httprequest = this . httprequest ;
2020-01-07 16:04:36 -05:00
this . httprequest . desktop . kvm . connectionBar . on ( 'close' , function ( ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 29 , null , "Remote Desktop Connection forcefully closed by local user (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2020-01-07 16:04:36 -05:00
for ( var i in this . httprequest . desktop . kvm . _pipedStreams ) {
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . _pipedStreams [ i ] . end ( ) ;
}
this . httprequest . desktop . kvm . end ( ) ;
} ) ;
break ;
}
}
2018-04-19 21:19:15 -04:00
}
2018-01-16 20:30:34 -05:00
} ;
2020-01-07 16:04:36 -05:00
if ( this . httprequest . desktop . kvm . hasOwnProperty ( 'connectionCount' ) ) {
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . connectionCount ++ ;
2020-07-13 14:30:27 -04:00
this . httprequest . desktop . kvm . rusers . push ( this . httprequest . realname ) ;
this . httprequest . desktop . kvm . users . push ( this . httprequest . username ) ;
this . httprequest . desktop . kvm . rusers . sort ( ) ;
this . httprequest . desktop . kvm . users . sort ( ) ;
2020-03-06 17:06:33 -05:00
} else {
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . connectionCount = 1 ;
2020-07-13 14:30:27 -04:00
this . httprequest . desktop . kvm . rusers = [ this . httprequest . realname ] ;
this . httprequest . desktop . kvm . users = [ this . httprequest . username ] ;
2019-10-11 13:53:42 -04:00
}
2018-11-27 20:13:01 -05:00
2019-03-09 17:28:08 -05:00
if ( ( this . httprequest . rights == 0xFFFFFFFF ) || ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) != 0 ) && ( ( this . httprequest . rights & MESHRIGHT _REMOTEVIEW ) == 0 ) ) ) {
2018-11-27 20:13:01 -05:00
// If we have remote control rights, pipe the KVM input
this . pipe ( this . httprequest . desktop . kvm , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
} else {
// We need to only pipe non-mouse & non-keyboard inputs.
2019-03-09 17:28:08 -05:00
//sendConsoleText('Warning: No Remote Desktop Input Rights.');
2018-11-27 20:13:01 -05:00
// TODO!!!
}
2019-06-14 19:33:53 -04:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
2020-04-28 15:09:08 -04:00
if ( this . httprequest . consent && ( this . httprequest . consent & 8 ) )
{
2019-06-14 19:33:53 -04:00
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
2020-05-03 15:54:51 -04:00
this . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : "Waiting for user to grant access..." , msgid : 1 } ) ) ;
2020-07-10 13:33:41 -04:00
var consentMessage = this . httprequest . realname + " requesting remote desktop access. Grant access?" , consentTitle = 'MeshCentral' ;
2020-06-13 14:36:50 -04:00
if ( this . httprequest . soptions != null ) {
if ( this . httprequest . soptions . consentTitle != null ) { consentTitle = this . httprequest . soptions . consentTitle ; }
2020-07-13 14:30:27 -04:00
if ( this . httprequest . soptions . consentMsgDesktop != null ) { consentMessage = this . httprequest . soptions . consentMsgDesktop . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
var pr = require ( 'message-box' ) . create ( consentTitle , consentMessage , 30 , null , tsid ) ;
2019-06-14 19:33:53 -04:00
pr . ws = this ;
this . pause ( ) ;
2020-04-28 15:09:08 -04:00
this . _consentpromise = pr ;
this . prependOnceListener ( 'end' , function ( ) { if ( this . _consentpromise && this . _consentpromise . close ) { this . _consentpromise . close ( ) ; } } ) ;
2019-06-14 19:33:53 -04:00
pr . then (
2020-04-28 15:09:08 -04:00
function ( )
{
2019-07-29 19:35:48 -04:00
// Success
2020-04-28 15:09:08 -04:00
this . ws . _consentpromise = null ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 30 , null , "Starting remote desktop after local user accepted (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2020-05-03 15:54:51 -04:00
this . ws . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : null , msgid : 0 } ) ) ;
2020-01-07 16:04:36 -05:00
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 1 ) ) {
2019-06-14 19:33:53 -04:00
// User Notifications is required
2020-07-10 13:33:41 -04:00
var notifyMessage = this . ws . httprequest . realname + " started a remote desktop session." , notifyTitle = "MeshCentral" ;
2020-06-13 14:36:50 -04:00
if ( this . ws . httprequest . soptions != null ) {
if ( this . ws . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . ws . httprequest . soptions . notifyTitle ; }
2020-07-13 14:30:27 -04:00
if ( this . ws . httprequest . soptions . notifyMsgDesktop != null ) { notifyMessage = this . ws . httprequest . soptions . notifyMsgDesktop . replace ( '{0}' , this . ws . httprequest . realname ) . replace ( '{1}' , this . ws . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
2020-09-30 19:59:34 -04:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage , tsid ) ; } catch ( e ) { }
2019-06-14 19:33:53 -04:00
}
2020-01-07 16:04:36 -05:00
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 0x40 ) ) {
2019-10-11 13:53:42 -04:00
// Connection Bar is required
2020-01-07 16:04:36 -05:00
if ( this . ws . httprequest . desktop . kvm . connectionBar ) {
2019-10-11 13:53:42 -04:00
this . ws . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . ws . httprequest . desktop . kvm . connectionBar . close ( ) ;
}
2020-01-07 16:04:36 -05:00
try {
2020-07-13 14:30:27 -04:00
this . ws . httprequest . desktop . kvm . connectionBar = require ( 'notifybar-desktop' ) ( this . ws . httprequest . privacybartext . replace ( '{0}' , this . ws . httprequest . desktop . kvm . users . join ( ', ' ) ) . replace ( '{1}' , this . ws . httprequest . desktop . kvm . rusers . join ( ', ' ) ) , require ( 'MeshAgent' ) . _tsid ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 31 , null , "Remote Desktop Connection Bar Activated/Updated (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2019-10-25 13:33:10 -04:00
}
2020-09-30 19:59:34 -04:00
catch ( e ) {
2020-01-07 16:04:36 -05:00
if ( process . platform != 'darwin' ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 32 , null , "Remote Desktop Connection Bar Failed or Not Supported (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2019-10-11 13:53:42 -04:00
}
2019-10-25 13:33:10 -04:00
}
2020-01-07 16:04:36 -05:00
if ( this . ws . httprequest . desktop . kvm . connectionBar ) {
2019-10-25 13:33:10 -04:00
this . ws . httprequest . desktop . kvm . connectionBar . httprequest = this . ws . httprequest ;
2020-01-07 16:04:36 -05:00
this . ws . httprequest . desktop . kvm . connectionBar . on ( 'close' , function ( ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 33 , null , "Remote Desktop Connection forcefully closed by local user (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2020-01-07 16:04:36 -05:00
for ( var i in this . httprequest . desktop . kvm . _pipedStreams ) {
2019-10-25 13:33:10 -04:00
this . httprequest . desktop . kvm . _pipedStreams [ i ] . end ( ) ;
}
this . httprequest . desktop . kvm . end ( ) ;
} ) ;
}
2019-10-11 13:53:42 -04:00
}
2019-06-14 19:33:53 -04:00
this . ws . httprequest . desktop . kvm . pipe ( this . ws , { dataTypeSkip : 1 } ) ;
this . ws . resume ( ) ;
} ,
2020-04-28 15:09:08 -04:00
function ( e )
{
2019-07-29 19:35:48 -04:00
// User Consent Denied/Failed
2020-04-28 15:09:08 -04:00
this . ws . _consentpromise = null ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 34 , null , "Failed to start remote desktop after local user rejected (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2020-05-03 15:54:51 -04:00
this . ws . end ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
2019-06-14 19:33:53 -04:00
} ) ;
2020-08-26 21:42:41 -04:00
} else {
2019-06-14 19:33:53 -04:00
// User Consent Prompt is not required
2020-01-07 16:04:36 -05:00
if ( this . httprequest . consent && ( this . httprequest . consent & 1 ) ) {
2019-06-14 19:33:53 -04:00
// User Notifications is required
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 35 , null , "Started remote desktop with toast notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2020-07-10 13:33:41 -04:00
var notifyMessage = this . httprequest . realname + " started a remote desktop session." , notifyTitle = "MeshCentral" ;
2020-06-13 14:36:50 -04:00
if ( this . httprequest . soptions != null ) {
if ( this . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . httprequest . soptions . notifyTitle ; }
2020-07-13 14:30:27 -04:00
if ( this . httprequest . soptions . notifyMsgDesktop != null ) { notifyMessage = this . httprequest . soptions . notifyMsgDesktop . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
2020-09-30 19:59:34 -04:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage , tsid ) ; } catch ( e ) { }
2020-01-07 16:04:36 -05:00
} else {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 36 , null , "Started remote desktop without notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2019-06-14 19:33:53 -04:00
}
2020-01-07 16:04:36 -05:00
if ( this . httprequest . consent && ( this . httprequest . consent & 0x40 ) ) {
2019-10-11 13:53:42 -04:00
// Connection Bar is required
2020-01-07 16:04:36 -05:00
if ( this . httprequest . desktop . kvm . connectionBar ) {
2019-10-11 13:53:42 -04:00
this . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . httprequest . desktop . kvm . connectionBar . close ( ) ;
}
2020-01-07 16:04:36 -05:00
try {
2020-07-13 14:30:27 -04:00
this . httprequest . desktop . kvm . connectionBar = require ( 'notifybar-desktop' ) ( this . httprequest . privacybartext . replace ( '{0}' , this . httprequest . desktop . kvm . rusers . join ( ', ' ) ) . replace ( '{1}' , this . httprequest . desktop . kvm . users . join ( ', ' ) ) , require ( 'MeshAgent' ) . _tsid ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 37 , null , "Remote Desktop Connection Bar Activated/Updated (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2019-10-25 13:33:10 -04:00
}
2020-09-30 19:59:34 -04:00
catch ( e ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 38 , null , "Remote Desktop Connection Bar Failed or not Supported (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2019-10-25 13:33:10 -04:00
}
2020-01-07 16:04:36 -05:00
if ( this . httprequest . desktop . kvm . connectionBar ) {
2019-10-25 13:33:10 -04:00
this . httprequest . desktop . kvm . connectionBar . httprequest = this . httprequest ;
2020-01-07 16:04:36 -05:00
this . httprequest . desktop . kvm . connectionBar . on ( 'close' , function ( ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 39 , null , "Remote Desktop Connection forcefully closed by local user (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2020-01-07 16:04:36 -05:00
for ( var i in this . httprequest . desktop . kvm . _pipedStreams ) {
2019-10-25 13:33:10 -04:00
this . httprequest . desktop . kvm . _pipedStreams [ i ] . end ( ) ;
}
this . httprequest . desktop . kvm . end ( ) ;
} ) ;
}
2019-10-11 13:53:42 -04:00
}
2019-06-14 19:33:53 -04:00
this . httprequest . desktop . kvm . pipe ( this , { dataTypeSkip : 1 } ) ;
}
2018-01-16 20:30:34 -05:00
this . removeAllListeners ( 'data' ) ;
this . on ( 'data' , onTunnelControlData ) ;
//this.write('MeshCore KVM Hello!1');
2019-05-20 19:00:33 -04:00
2020-08-13 15:29:18 -04:00
} else if ( this . httprequest . protocol == 5 ) {
2020-05-21 16:22:58 -04:00
//
// Remote Files
//
2019-03-01 14:47:58 -05:00
// Check user access rights for files
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NOFILES ) != 0 ) ) ) {
2018-11-27 20:13:01 -05:00
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
2019-03-01 14:47:58 -05:00
this . httprequest . s . end ( ) ;
2020-01-07 16:04:36 -05:00
sendConsoleText ( "Error: No files control rights." ) ;
2018-11-27 20:13:01 -05:00
return ;
}
2020-05-21 16:22:58 -04:00
this . descriptorMetadata = "Remote Files" ;
2020-05-15 18:23:37 -04:00
// Add the files session to the count to update the server
if ( this . httprequest . userid != null ) {
if ( tunnelUserCount . files [ this . httprequest . userid ] == null ) { tunnelUserCount . files [ this . httprequest . userid ] = 1 ; } else { tunnelUserCount . files [ this . httprequest . userid ] ++ ; }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'files' , value : tunnelUserCount . files } ) ; } catch ( e ) { }
2020-05-15 18:23:37 -04:00
}
this . end = function ( ) {
// Remove the files session from the count to update the server
if ( this . httprequest . userid != null ) {
if ( tunnelUserCount . files [ this . httprequest . userid ] != null ) { tunnelUserCount . files [ this . httprequest . userid ] -- ; if ( tunnelUserCount . files [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . files [ this . httprequest . userid ] ; } }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'files' , value : tunnelUserCount . files } ) ; } catch ( e ) { }
2020-05-15 18:23:37 -04:00
}
} ;
2019-06-14 19:33:53 -04:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
2019-10-10 14:13:25 -04:00
if ( this . httprequest . consent && ( this . httprequest . consent & 32 ) ) {
2019-06-14 19:33:53 -04:00
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
2020-05-03 15:54:51 -04:00
this . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : "Waiting for user to grant access..." , msgid : 1 } ) ) ;
2020-07-10 13:33:41 -04:00
var consentMessage = this . httprequest . realname + " requesting remote file Access. Grant access?" , consentTitle = 'MeshCentral' ;
2020-06-13 14:36:50 -04:00
if ( this . httprequest . soptions != null ) {
if ( this . httprequest . soptions . consentTitle != null ) { consentTitle = this . httprequest . soptions . consentTitle ; }
2020-07-13 14:30:27 -04:00
if ( this . httprequest . soptions . consentMsgFiles != null ) { consentMessage = this . httprequest . soptions . consentMsgFiles . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
var pr = require ( 'message-box' ) . create ( consentTitle , consentMessage , 30 ) ;
2019-06-14 19:33:53 -04:00
pr . ws = this ;
this . pause ( ) ;
2020-04-28 15:15:36 -04:00
this . _consentpromise = pr ;
this . prependOnceListener ( 'end' , function ( ) { if ( this . _consentpromise && this . _consentpromise . close ) { this . _consentpromise . close ( ) ; } } ) ;
2019-06-14 19:33:53 -04:00
pr . then (
2020-04-28 15:15:36 -04:00
function ( )
{
2019-07-29 19:35:48 -04:00
// Success
2020-04-28 15:15:36 -04:00
this . ws . _consentpromise = null ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 40 , null , "Starting remote files after local user accepted (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2019-06-14 19:33:53 -04:00
this . ws . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : null } ) ) ;
2019-07-29 19:35:48 -04:00
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 4 ) ) {
2019-06-14 19:33:53 -04:00
// User Notifications is required
2020-07-10 13:33:41 -04:00
var notifyMessage = this . ws . httprequest . realname + " started a remote file session." , notifyTitle = "MeshCentral" ;
2020-06-13 14:36:50 -04:00
if ( this . ws . httprequest . soptions != null ) {
if ( this . ws . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . ws . httprequest . soptions . notifyTitle ; }
2020-07-13 14:30:27 -04:00
if ( this . ws . httprequest . soptions . notifyMsgFiles != null ) { notifyMessage = this . ws . httprequest . soptions . notifyMsgFiles . replace ( '{0}' , this . ws . httprequest . realname ) . replace ( '{1}' , this . ws . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
2020-09-30 19:59:34 -04:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage ) ; } catch ( e ) { }
2019-06-14 19:33:53 -04:00
}
this . ws . resume ( ) ;
} ,
2020-04-28 15:15:36 -04:00
function ( e )
{
2019-07-29 19:35:48 -04:00
// User Consent Denied/Failed
2020-04-28 15:15:36 -04:00
this . ws . _consentpromise = null ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 41 , null , "Failed to start remote files after local user rejected (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2020-05-03 15:54:51 -04:00
this . ws . end ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
2019-06-14 19:33:53 -04:00
} ) ;
2019-07-29 19:35:48 -04:00
} else {
2019-06-14 19:33:53 -04:00
// User Consent Prompt is not required
if ( this . httprequest . consent && ( this . httprequest . consent & 4 ) ) {
// User Notifications is required
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 42 , null , "Started remote files with toast notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2020-07-10 13:33:41 -04:00
var notifyMessage = this . httprequest . realname + " started a remote file session." , notifyTitle = "MeshCentral" ;
2020-06-13 14:36:50 -04:00
if ( this . httprequest . soptions != null ) {
if ( this . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . httprequest . soptions . notifyTitle ; }
2020-07-13 14:30:27 -04:00
if ( this . httprequest . soptions . notifyMsgFiles != null ) { notifyMessage = this . httprequest . soptions . notifyMsgFiles . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2020-06-13 14:36:50 -04:00
}
2020-09-30 19:59:34 -04:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage ) ; } catch ( e ) { }
2019-07-29 19:35:48 -04:00
} else {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 43 , null , "Started remote files without notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2019-06-14 19:33:53 -04:00
}
this . resume ( ) ;
2019-04-12 17:19:03 -04:00
}
2017-08-28 12:27:45 -04:00
// Setup files
// NOP
}
} else if ( this . httprequest . protocol == 1 ) {
// Send data into terminal stdin
//this.write(data); // Echo back the keys (Does not seem to be a good idea)
} else if ( this . httprequest . protocol == 2 ) {
// Send data into remote desktop
if ( this . httprequest . desktop . state == 0 ) {
2020-08-06 16:23:17 -04:00
this . write ( Buffer . from ( String . fromCharCode ( 0x11 , 0xFE , 0x00 , 0x00 , 0x4D , 0x45 , 0x53 , 0x48 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ) ) ) ;
2017-08-28 12:27:45 -04:00
this . httprequest . desktop . state = 1 ;
} else {
this . httprequest . desktop . write ( data ) ;
}
} else if ( this . httprequest . protocol == 5 ) {
// Process files commands
var cmd = null ;
try { cmd = JSON . parse ( data ) ; } catch ( e ) { } ;
2018-02-11 20:13:26 -05:00
if ( cmd == null ) { return ; }
if ( ( cmd . ctrlChannel == '102938' ) || ( ( cmd . type == 'offer' ) && ( cmd . sdp != null ) ) ) { onTunnelControlData ( cmd , this ) ; return ; } // If this is control data, handle it now.
if ( cmd . action == undefined ) { return ; }
//sendConsoleText('CMD: ' + JSON.stringify(cmd));
2017-10-16 23:11:03 -04:00
if ( ( cmd . path != null ) && ( process . platform != 'win32' ) && ( cmd . path [ 0 ] != '/' ) ) { cmd . path = '/' + cmd . path ; } // Add '/' to paths on non-windows
2018-02-11 20:13:26 -05:00
//console.log(objToString(cmd, 0, ' '));
2017-08-28 12:27:45 -04:00
switch ( cmd . action ) {
case 'ls' : {
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04:00
// Close the watcher if required
var samepath = ( ( this . httprequest . watcher != undefined ) && ( cmd . path == this . httprequest . watcher . path ) ) ;
if ( ( this . httprequest . watcher != undefined ) && ( samepath == false ) ) {
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
delete this . httprequest . watcher ;
}
2017-12-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04:00
// Send the folder content to the browser
var response = getDirectoryInfo ( cmd . path ) ;
if ( cmd . reqid != undefined ) { response . reqid = cmd . reqid ; }
2020-08-06 16:23:17 -04:00
this . write ( Buffer . from ( JSON . stringify ( response ) ) ) ;
2019-10-10 14:13:25 -04:00
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04:00
// Start the directory watcher
if ( ( cmd . path != '' ) && ( samepath == false ) ) {
var watcher = fs . watch ( cmd . path , onFileWatcher ) ;
watcher . tunnel = this . httprequest ;
watcher . path = cmd . path ;
this . httprequest . watcher = watcher ;
//console.log('Starting watcher: ' + this.httprequest.watcher.path);
}
2017-12-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04:00
break ;
}
case 'mkdir' : {
// Create a new empty folder
fs . mkdirSync ( cmd . path ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 44 , [ cmd . path ] , "Create folder: \"" + cmd . path + "\"" , this . httprequest ) ;
2017-08-28 12:27:45 -04:00
break ;
}
case 'rm' : {
2018-09-25 14:51:40 -04:00
// Delete, possibly recursive delete
2017-08-28 12:27:45 -04:00
for ( var i in cmd . delfiles ) {
2019-07-29 19:35:48 -04:00
var p = obj . path . join ( cmd . path , cmd . delfiles [ i ] ) , delcount = 0 ;
try { delcount = deleteFolderRecursive ( p , cmd . rec ) ; } catch ( e ) { }
2019-08-01 18:35:23 -04:00
if ( ( delcount == 1 ) && ! cmd . rec ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 45 , [ p ] , "Delete: \"" + p + "\"" , this . httprequest ) ;
2019-08-01 18:35:23 -04:00
} else {
2020-09-08 21:59:58 -04:00
if ( cmd . rec ) {
MeshServerLogEx ( 46 , [ p , delcount ] , "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed" , this . httprequest ) ;
} else {
MeshServerLogEx ( 47 , [ p , delcount ] , "Delete: \"" + p + "\", " + delcount + " element(s) removed" , this . httprequest ) ;
}
2019-08-01 18:35:23 -04:00
}
2017-08-28 12:27:45 -04:00
}
break ;
}
2020-07-08 02:56:08 -04:00
case 'markcoredump' : {
// If we are asking for the coredump file, set the right path.
var coreDumpPath = null ;
if ( process . platform == 'win32' ) {
if ( fs . existsSync ( process . coreDumpLocation ) ) { coreDumpPath = process . coreDumpLocation ; }
} else {
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) ) { coreDumpPath = process . cwd ( ) + 'core' ; }
}
if ( coreDumpPath != null ) { db . Put ( 'CoreDumpTime' , require ( 'fs' ) . statSync ( coreDumpPath ) . mtime ) ; }
break ;
}
2017-08-28 12:27:45 -04:00
case 'rename' : {
// Rename a file or folder
var oldfullpath = obj . path . join ( cmd . path , cmd . oldname ) ;
var newfullpath = obj . path . join ( cmd . path , cmd . newname ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 48 , [ oldfullpath , cmd . newname ] , 'Rename: \"' + oldfullpath + '\" to \"' + cmd . newname + '\"' , this . httprequest ) ;
2017-08-28 12:27:45 -04:00
try { fs . renameSync ( oldfullpath , newfullpath ) ; } catch ( e ) { console . log ( e ) ; }
break ;
}
2018-02-11 20:13:26 -05:00
case 'download' : {
// Download a file
var sendNextBlock = 0 ;
if ( cmd . sub == 'start' ) { // Setup the download
2020-07-08 02:56:08 -04:00
if ( ( cmd . path == null ) && ( cmd . ask == 'coredump' ) ) { // If we are asking for the coredump file, set the right path.
if ( process . platform == 'win32' ) {
if ( fs . existsSync ( process . coreDumpLocation ) ) { cmd . path = process . coreDumpLocation ; }
} else {
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) ) { cmd . path = process . cwd ( ) + 'core' ; }
}
}
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 49 , [ cmd . path ] , 'Download: \"' + cmd . path + '\"' , this . httprequest ) ;
2020-07-08 02:56:08 -04:00
if ( ( cmd . path == null ) || ( this . filedownload != null ) ) { this . write ( { action : 'download' , sub : 'cancel' , id : this . filedownload . id } ) ; delete this . filedownload ; }
2018-02-11 20:13:26 -05:00
this . filedownload = { id : cmd . id , path : cmd . path , ptr : 0 }
try { this . filedownload . f = fs . openSync ( this . filedownload . path , 'rbN' ) ; } catch ( e ) { this . write ( { action : 'download' , sub : 'cancel' , id : this . filedownload . id } ) ; delete this . filedownload ; }
if ( this . filedownload ) { this . write ( { action : 'download' , sub : 'start' , id : cmd . id } ) ; }
} else if ( ( this . filedownload != null ) && ( cmd . id == this . filedownload . id ) ) { // Download commands
2020-07-08 02:56:08 -04:00
if ( cmd . sub == 'startack' ) { sendNextBlock = ( ( typeof cmd . ack == 'number' ) ? cmd . ack : 8 ) ; } else if ( cmd . sub == 'stop' ) { delete this . filedownload ; } else if ( cmd . sub == 'ack' ) { sendNextBlock = 1 ; }
2018-02-11 20:13:26 -05:00
}
// Send the next download block(s)
while ( sendNextBlock > 0 ) {
sendNextBlock -- ;
2020-08-06 16:23:17 -04:00
var buf = Buffer . alloc ( 16384 ) ;
2020-07-21 20:09:11 -04:00
var len = fs . readSync ( this . filedownload . f , buf , 4 , 16380 , null ) ;
2018-02-11 20:13:26 -05:00
this . filedownload . ptr += len ;
2020-07-21 20:09:11 -04:00
if ( len < 16380 ) { buf . writeInt32BE ( 0x01000001 , 0 ) ; fs . closeSync ( this . filedownload . f ) ; delete this . filedownload ; sendNextBlock = 0 ; } else { buf . writeInt32BE ( 0x01000000 , 0 ) ; }
2018-02-13 15:28:11 -05:00
this . write ( buf . slice ( 0 , len + 4 ) ) ; // Write as binary
2018-02-11 20:13:26 -05:00
}
break ;
}
/ *
2017-08-28 12:27:45 -04:00
case 'download' : {
// Packet download of a file, agent to browser
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
//console.log('Download: ' + filepath);
2020-08-06 16:23:17 -04:00
try { this . httprequest . downloadFile = fs . openSync ( filepath , 'rbN' ) ; } catch ( e ) { this . write ( Buffer . from ( JSON . stringify ( { action : 'downloaderror' , reqid : cmd . reqid } ) ) ) ; break ; }
2017-08-28 12:27:45 -04:00
this . httprequest . downloadFileId = cmd . reqid ;
this . httprequest . downloadFilePtr = 0 ;
2020-08-06 16:23:17 -04:00
if ( this . httprequest . downloadFile ) { this . write ( Buffer . from ( JSON . stringify ( { action : 'downloadstart' , reqid : this . httprequest . downloadFileId } ) ) ) ; }
2017-08-28 12:27:45 -04:00
break ;
}
case 'download2' : {
// Stream download of a file, agent to browser
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
try { this . httprequest . downloadFile = fs . createReadStream ( filepath , { flags : 'rbN' } ) ; } catch ( e ) { console . log ( e ) ; }
this . httprequest . downloadFile . pipe ( this ) ;
this . httprequest . downloadFile . end = function ( ) { }
break ;
}
2018-02-11 20:13:26 -05:00
* /
2017-08-28 12:27:45 -04:00
case 'upload' : {
// Upload a file, browser to agent
2020-07-22 14:19:39 -04:00
if ( this . httprequest . uploadFile != null ) { fs . closeSync ( this . httprequest . uploadFile ) ; delete this . httprequest . uploadFile ; }
2017-08-28 12:27:45 -04:00
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
2020-08-13 15:29:18 -04:00
this . httprequest . uploadFilePath = filepath ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 50 , [ filepath ] , 'Upload: \"' + filepath + '\"' , this . httprequest ) ;
2020-08-06 16:23:17 -04:00
try { this . httprequest . uploadFile = fs . openSync ( filepath , 'wbN' ) ; } catch ( e ) { this . write ( Buffer . from ( JSON . stringify ( { action : 'uploaderror' , reqid : cmd . reqid } ) ) ) ; break ; }
2017-08-28 12:27:45 -04:00
this . httprequest . uploadFileid = cmd . reqid ;
2020-08-06 16:23:17 -04:00
if ( this . httprequest . uploadFile ) { this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadstart' , reqid : this . httprequest . uploadFileid } ) ) ) ; }
2017-08-28 12:27:45 -04:00
break ;
}
2020-08-13 15:29:18 -04:00
case 'uploaddone' : {
// Indicates that an upload is done
if ( this . httprequest . uploadFile ) {
fs . closeSync ( this . httprequest . uploadFile ) ;
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploaddone' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Indicate that we closed the file.
delete this . httprequest . uploadFile ;
delete this . httprequest . uploadFileid ;
delete this . httprequest . uploadFilePath ;
}
break ;
}
case 'uploadcancel' : {
// Indicates that an upload is canceled
if ( this . httprequest . uploadFile ) {
fs . closeSync ( this . httprequest . uploadFile ) ;
fs . unlinkSync ( this . httprequest . uploadFilePath ) ;
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadcancel' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Indicate that we closed the file.
delete this . httprequest . uploadFile ;
delete this . httprequest . uploadFileid ;
delete this . httprequest . uploadFilePath ;
}
break ;
}
2018-04-02 18:34:32 -04:00
case 'copy' : {
// Copy a bunch of files from scpath to dspath
for ( var i in cmd . names ) {
var sc = obj . path . join ( cmd . scpath , cmd . names [ i ] ) , ds = obj . path . join ( cmd . dspath , cmd . names [ i ] ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 51 , [ sc , ds ] , 'Copy: \"' + sc + '\" to \"' + ds + '\"' , this . httprequest ) ;
2018-04-02 18:34:32 -04:00
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; } catch ( e ) { } }
}
break ;
}
case 'move' : {
// Move a bunch of files from scpath to dspath
for ( var i in cmd . names ) {
var sc = obj . path . join ( cmd . scpath , cmd . names [ i ] ) , ds = obj . path . join ( cmd . dspath , cmd . names [ i ] ) ;
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 52 , [ sc , ds ] , 'Move: \"' + sc + '\" to \"' + ds + '\"' , this . httprequest ) ;
2018-04-02 18:34:32 -04:00
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; fs . unlinkSync ( sc ) ; } catch ( e ) { } }
}
break ;
}
2020-08-13 16:13:21 -04:00
case 'zip' :
// Zip a bunch of files
2020-08-19 17:01:05 -04:00
if ( this . zip != null ) return ; // Zip operating is currently running, exit now.
// Check that the specified files exist & build full paths
2020-08-13 17:03:41 -04:00
var fp , stat , p = [ ] ;
2020-08-19 17:01:05 -04:00
for ( var i in cmd . files ) { fp = cmd . path + '/' + cmd . files [ i ] ; stat = null ; try { stat = fs . statSync ( fp ) ; } catch ( e ) { } if ( stat != null ) { p . push ( fp ) ; } }
if ( p . length == 0 ) return ; // No files, quit now.
// Setup file compression
2020-08-13 17:03:41 -04:00
var ofile = cmd . path + '/' + cmd . output ;
this . write ( Buffer . from ( JSON . stringify ( { action : 'dialogmessage' , msg : 'zipping' } ) ) ) ;
2020-08-28 21:29:32 -04:00
this . zipfile = ofile ;
delete this . zipcancel ;
2020-08-13 16:13:21 -04:00
var out = require ( 'fs' ) . createWriteStream ( ofile , { flags : 'wb' } ) ;
2020-08-13 17:03:41 -04:00
out . xws = this ;
2020-08-28 21:29:32 -04:00
out . on ( 'close' , function ( ) {
this . xws . write ( Buffer . from ( JSON . stringify ( { action : 'dialogmessage' , msg : null } ) ) ) ;
this . xws . write ( Buffer . from ( JSON . stringify ( { action : 'refresh' } ) ) ) ;
if ( this . xws . zipcancel === true ) { fs . unlinkSync ( this . xws . zipfile ) ; } // Delete the complete file.
delete this . xws . zipcancel ;
delete this . xws . zipfile ;
delete this . xws . zip ;
} ) ;
2020-08-19 17:01:05 -04:00
this . zip = require ( 'zip-writer' ) . write ( { files : p , basePath : cmd . path } ) ;
this . zip . xws = this ;
2020-09-03 02:06:08 -04:00
this . zip . on ( 'progress' , require ( 'events' ) . moderated ( function ( name , p ) { this . xws . write ( Buffer . from ( JSON . stringify ( { action : 'dialogmessage' , msg : 'zippingFile' , file : ( ( process . platform == 'win32' ) ? ( name . split ( '/' ) . join ( '\\' ) ) : name ) , progress : p } ) ) ) ; } , 1000 ) ) ;
2020-08-18 14:10:21 -04:00
this . zip . pipe ( out ) ;
2020-08-13 16:13:21 -04:00
break ;
2020-08-13 17:03:41 -04:00
case 'cancel' :
2020-08-19 17:01:05 -04:00
// Cancel zip operation if present
2020-09-30 19:59:34 -04:00
try { this . zipcancel = true ; this . zip . cancel ( function ( ) { } ) ; } catch ( e ) { }
2020-08-19 17:01:05 -04:00
this . zip = null ;
2020-08-13 17:03:41 -04:00
break ;
2019-01-04 20:59:13 -05:00
default :
// Unknown action, ignore it.
break ;
2017-08-28 12:27:45 -04:00
}
2019-11-26 13:38:06 -05:00
} else if ( this . httprequest . protocol == 7 ) { // Plugin data exchange
2019-10-10 14:13:25 -04:00
var cmd = null ;
try { cmd = JSON . parse ( data ) ; } catch ( e ) { } ;
if ( cmd == null ) { return ; }
if ( ( cmd . ctrlChannel == '102938' ) || ( ( cmd . type == 'offer' ) && ( cmd . sdp != null ) ) ) { onTunnelControlData ( cmd , this ) ; return ; } // If this is control data, handle it now.
if ( cmd . action == undefined ) return ;
switch ( cmd . action ) {
case 'plugin' : {
try { require ( cmd . plugin ) . consoleaction ( cmd , null , null , this ) ; } catch ( e ) { throw e ; }
break ;
}
default : {
// probably shouldn't happen, but just in case this feature is expanded
}
}
2017-08-28 12:27:45 -04:00
}
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
}
}
2018-01-16 20:30:34 -05:00
2019-03-14 15:28:58 -04:00
// Delete a directory with a files and directories within it
function deleteFolderRecursive ( path , rec ) {
2019-07-29 19:35:48 -04:00
var count = 0 ;
2019-03-14 15:28:58 -04:00
if ( fs . existsSync ( path ) ) {
if ( rec == true ) {
fs . readdirSync ( obj . path . join ( path , '*' ) ) . forEach ( function ( file , index ) {
var curPath = obj . path . join ( path , file ) ;
if ( fs . statSync ( curPath ) . isDirectory ( ) ) { // recurse
2019-07-29 19:35:48 -04:00
count += deleteFolderRecursive ( curPath , true ) ;
2019-03-14 15:28:58 -04:00
} else { // delete file
fs . unlinkSync ( curPath ) ;
2019-07-29 19:35:48 -04:00
count ++ ;
2019-03-14 15:28:58 -04:00
}
} ) ;
}
fs . unlinkSync ( path ) ;
2019-07-29 19:35:48 -04:00
count ++ ;
2019-03-14 15:28:58 -04:00
}
2019-07-29 19:35:48 -04:00
return count ;
2019-03-14 15:28:58 -04:00
} ;
2018-01-18 18:43:43 -05:00
// Called when receiving control data on WebRTC
function onTunnelWebRTCControlData ( data ) {
if ( typeof data != 'string' ) return ;
var obj ;
2018-07-23 20:34:24 -04:00
try { obj = JSON . parse ( data ) ; } catch ( e ) { sendConsoleText ( 'Invalid control JSON on WebRTC: ' + data ) ; return ; }
2018-01-18 18:43:43 -05:00
if ( obj . type == 'close' ) {
2018-02-05 14:56:29 -05:00
//sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2018-01-18 18:43:43 -05:00
try { this . close ( ) ; } catch ( e ) { }
try { this . xrtc . close ( ) ; } catch ( e ) { }
}
}
// Called when receiving control data on websocket
2018-02-11 20:13:26 -05:00
function onTunnelControlData ( data , ws ) {
2018-01-18 18:43:43 -05:00
var obj ;
2018-02-11 20:13:26 -05:00
if ( ws == null ) { ws = this ; }
2018-07-23 20:34:24 -04:00
if ( typeof data == 'string' ) { try { obj = JSON . parse ( data ) ; } catch ( e ) { sendConsoleText ( 'Invalid control JSON: ' + data ) ; return ; } }
2018-02-11 20:13:26 -05:00
else if ( typeof data == 'object' ) { obj = data ; } else { return ; }
//sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
//console.log('onTunnelControlData: ' + JSON.stringify(data));
2018-01-18 18:43:43 -05:00
2018-07-23 20:34:24 -04:00
if ( obj . action ) {
switch ( obj . action ) {
case 'lock' : {
// Lock the current user out of the desktop
try {
if ( process . platform == 'win32' ) {
2020-09-08 21:59:58 -04:00
MeshServerLogEx ( 53 , null , "Locking remote user out of desktop" , ws . httprequest ) ;
2018-07-23 20:34:24 -04:00
var child = require ( 'child_process' ) ;
child . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ '/c' , 'RunDll32.exe user32.dll,LockWorkStation' ] , { type : 1 } ) ;
}
} catch ( e ) { }
break ;
}
2019-01-04 20:59:13 -05:00
default :
// Unknown action, ignore it.
break ;
2018-07-23 20:34:24 -04:00
}
return ;
}
2020-01-24 15:49:41 -05:00
switch ( obj . type ) {
case 'options' : {
// These are additional connection options passed in the control channel.
//sendConsoleText('options: ' + JSON.stringify(obj));
delete obj . type ;
ws . httprequest . xoptions = obj ;
2020-04-14 22:12:14 -04:00
// Set additional user consent options if present
2020-04-15 15:36:22 -04:00
if ( ( obj != null ) && ( typeof obj . consent == 'number' ) ) { ws . httprequest . consent |= obj . consent ; }
2020-04-14 22:12:14 -04:00
2020-01-24 15:49:41 -05:00
break ;
2018-02-05 14:56:29 -05:00
}
2020-01-24 15:49:41 -05:00
case 'close' : {
// We received the close on the websocket
//sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
try { ws . close ( ) ; } catch ( e ) { }
break ;
}
case 'termsize' : {
2020-01-25 06:08:01 -05:00
// Indicates a change in terminal size
2020-01-28 18:08:51 -05:00
if ( process . platform == 'win32' )
{
2020-01-30 14:00:38 -05:00
if ( ws . httprequest . _dispatcher == null ) return ;
2020-01-25 06:08:01 -05:00
//sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2020-01-30 14:00:38 -05:00
if ( ws . httprequest . _dispatcher . invoke ) { ws . httprequest . _dispatcher . invoke ( 'resizeTerminal' , [ obj . cols , obj . rows ] ) ; }
2020-04-15 22:05:46 -04:00
} else
{
2020-01-28 18:08:51 -05:00
if ( ws . httprequest . process == null || ws . httprequest . process . pty == 0 ) return ;
//sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
if ( ws . httprequest . process . tcsetsize ) { ws . httprequest . process . tcsetsize ( obj . rows , obj . cols ) ; }
2018-12-12 18:34:42 -05:00
}
2020-01-24 15:49:41 -05:00
break ;
2018-01-18 18:43:43 -05:00
}
2020-01-24 15:49:41 -05:00
case 'webrtc0' : { // Browser indicates we can start WebRTC switch-over.
if ( ws . httprequest . protocol == 1 ) { // Terminal
// This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
if ( process . platform == 'win32' ) {
ws . httprequest . _term . unpipe ( ws ) ;
} else {
ws . httprequest . process . stdout . unpipe ( ws ) ;
ws . httprequest . process . stderr . unpipe ( ws ) ;
}
} else if ( ws . httprequest . protocol == 2 ) { // Desktop
// This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
ws . httprequest . desktop . kvm . unpipe ( ws ) ;
} else {
// Switch things around so all WebRTC data goes to onTunnelData().
ws . rtcchannel . httprequest = ws . httprequest ;
ws . rtcchannel . removeAllListeners ( 'data' ) ;
ws . rtcchannel . on ( 'data' , onTunnelData ) ;
2018-12-12 18:34:42 -05:00
}
2020-01-24 15:49:41 -05:00
ws . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}" ) ; // End of data marker
break ;
}
case 'webrtc1' : {
if ( ( ws . httprequest . protocol == 1 ) || ( ws . httprequest . protocol == 6 ) ) { // Terminal
// Switch the user input from websocket to webrtc at this point.
if ( process . platform == 'win32' ) {
ws . unpipe ( ws . httprequest . _term ) ;
ws . rtcchannel . pipe ( ws . httprequest . _term , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
} else {
ws . unpipe ( ws . httprequest . process . stdin ) ;
ws . rtcchannel . pipe ( ws . httprequest . process . stdin , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
}
ws . resume ( ) ; // Resume the websocket to keep receiving control data
} else if ( ws . httprequest . protocol == 2 ) { // Desktop
// Switch the user input from websocket to webrtc at this point.
ws . unpipe ( ws . httprequest . desktop . kvm ) ;
try { ws . webrtc . rtcchannel . pipe ( ws . httprequest . desktop . kvm , { dataTypeSkip : 1 , end : false } ) ; } catch ( e ) { sendConsoleText ( 'EX2' ) ; } // 0 = Binary, 1 = Text.
ws . resume ( ) ; // Resume the websocket to keep receiving control data
2018-12-12 18:34:42 -05:00
}
2020-01-24 15:49:41 -05:00
ws . write ( '{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}' ) ; // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
break ;
2018-01-18 18:43:43 -05:00
}
2020-01-24 15:49:41 -05:00
case 'webrtc2' : {
// Other side received websocket end of data marker, start sending data on WebRTC channel
if ( ( ws . httprequest . protocol == 1 ) || ( ws . httprequest . protocol == 6 ) ) { // Terminal
if ( process . platform == 'win32' ) {
ws . httprequest . _term . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
} else {
ws . httprequest . process . stdout . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
ws . httprequest . process . stderr . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
2019-01-16 20:58:48 -05:00
}
2020-01-24 15:49:41 -05:00
} else if ( ws . httprequest . protocol == 2 ) { // Desktop
ws . httprequest . desktop . kvm . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
}
break ;
}
case 'offer' : {
// This is a WebRTC offer.
if ( ( ws . httprequest . protocol == 1 ) || ( ws . httprequest . protocol == 6 ) ) return ; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
ws . webrtc = rtc . createConnection ( ) ;
ws . webrtc . websocket = ws ;
ws . webrtc . on ( 'connected' , function ( ) { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ } ) ;
ws . webrtc . on ( 'disconnected' , function ( ) { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ } ) ;
ws . webrtc . on ( 'dataChannel' , function ( rtcchannel ) {
//sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2020-05-18 05:39:30 -04:00
rtcchannel . maxFragmentSize = 32768 ;
2020-01-24 15:49:41 -05:00
rtcchannel . xrtc = this ;
rtcchannel . websocket = this . websocket ;
this . rtcchannel = rtcchannel ;
this . websocket . rtcchannel = rtcchannel ;
this . websocket . rtcchannel . on ( 'data' , onTunnelWebRTCControlData ) ;
this . websocket . rtcchannel . on ( 'end' , function ( ) {
// The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
//sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2020-04-28 14:36:04 -04:00
if ( this . websocket . desktop && this . websocket . desktop . kvm )
{
try
{
this . unpipe ( this . websocket . desktop . kvm ) ;
this . websocket . httprequest . desktop . kvm . unpipe ( this ) ;
}
2020-09-30 19:59:34 -04:00
catch ( e ) { }
2020-01-24 15:49:41 -05:00
}
} ) ;
this . websocket . write ( '{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}' ) ; // Indicate we are ready for WebRTC switch-over.
2019-01-03 17:46:52 -05:00
} ) ;
2020-01-24 15:49:41 -05:00
var sdp = null ;
2020-09-30 19:59:34 -04:00
try { sdp = ws . webrtc . setOffer ( obj . sdp ) ; } catch ( e ) { }
2020-01-24 15:49:41 -05:00
if ( sdp != null ) { ws . write ( { type : 'answer' , ctrlChannel : '102938' , sdp : sdp } ) ; }
break ;
}
2020-07-19 17:45:29 -04:00
case 'ping' : {
ws . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}" ) ; // Send pong response
break ;
}
case 'pong' : { // NOP
break ;
}
2020-04-14 06:08:53 -04:00
case 'rtt' : {
ws . write ( { type : 'rtt' , ctrlChannel : '102938' , time : obj . time } ) ;
2020-04-14 01:48:20 -04:00
break ;
}
2018-01-16 20:30:34 -05:00
}
}
2017-08-28 12:27:45 -04:00
// Console state
var consoleWebSockets = { } ;
var consoleHttpRequest = null ;
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Console HTTP response
function consoleHttpResponse ( response ) {
response . data = function ( data ) { sendConsoleText ( rstr2hex ( buf2rstr ( data ) ) , this . sessionid ) ; consoleHttpRequest = null ; }
response . close = function ( ) { sendConsoleText ( 'httprequest.response.close' , this . sessionid ) ; consoleHttpRequest = null ; }
} ;
2018-03-12 21:16:06 -04:00
2018-12-07 19:36:27 -05:00
// Open a web browser to a specified URL on current user's desktop
function openUserDesktopUrl ( url ) {
2020-10-05 14:40:29 -04:00
if ( ( url . toLowerCase ( ) . startsWith ( 'http://' ) == false ) && ( url . toLowerCase ( ) . startsWith ( 'https://' ) == false ) ) { return null ; }
2018-12-07 19:36:27 -05:00
var child = null ;
try {
switch ( process . platform ) {
case 'win32' :
2019-09-23 16:07:13 -04:00
var user = require ( 'user-sessions' ) . getUsername ( require ( 'user-sessions' ) . consoleUid ( ) ) ;
child = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ 'cmd' ] ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . stdout . on ( 'data' , function ( ) { } ) ;
child . stdin . write ( 'SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 /RU ' + user + ' /TR "' + process . env [ 'windir' ] + '\\system32\\cmd.exe /C START ' + url + '"\r\n' ) ;
child . stdin . write ( 'SCHTASKS /RUN /TN MeshChatTask\r\n' ) ;
child . stdin . write ( 'SCHTASKS /DELETE /F /TN MeshChatTask\r\n' ) ;
child . stdin . write ( 'exit\r\n' ) ;
child . waitExit ( ) ;
2018-12-07 19:36:27 -05:00
break ;
case 'linux' :
2019-01-15 12:20:21 -05:00
child = require ( 'child_process' ) . execFile ( '/usr/bin/xdg-open' , [ 'xdg-open' , url ] , { uid : require ( 'user-sessions' ) . consoleUid ( ) } ) ;
2018-12-07 19:36:27 -05:00
break ;
case 'darwin' :
child = require ( 'child_process' ) . execFile ( '/usr/bin/open' , [ 'open' , url ] , { uid : require ( 'user-sessions' ) . consoleUid ( ) } ) ;
break ;
2019-01-04 20:59:13 -05:00
default :
// Unknown platform, ignore this command.
break ;
2018-12-07 19:36:27 -05:00
}
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2018-12-07 19:36:27 -05:00
return child ;
}
2017-08-28 12:27:45 -04:00
// Process a mesh agent console command
function processConsoleCommand ( cmd , args , rights , sessionid ) {
try {
var response = null ;
switch ( cmd ) {
case 'help' : { // Displays available commands
2020-07-31 21:05:00 -04:00
var fin = '' , f = '' , availcommands = 'coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg' ;
2020-05-17 03:22:26 -04:00
if ( process . platform == 'win32' ) { availcommands += ',safemode,wpfhwacceleration,uac' ; }
2020-04-24 19:53:58 -04:00
if ( process . platform != 'freebsd' ) { availcommands += ',vm' ; }
2020-04-13 01:53:49 -04:00
if ( require ( 'MeshAgent' ) . maxKvmTileSize != null ) { availcommands += ',kvmmode' ; }
2020-09-30 19:59:34 -04:00
try { require ( 'zip-reader' ) ; availcommands += ',zip,unzip' ; } catch ( e ) { }
2020-04-13 01:53:49 -04:00
2019-10-01 17:54:38 -04:00
availcommands = availcommands . split ( ',' ) . sort ( ) ;
while ( availcommands . length > 0 ) {
2019-12-27 18:18:43 -05:00
if ( f . length > 90 ) { fin += ( f + ',\r\n' ) ; f = '' ; }
2019-09-27 13:34:07 -04:00
f += ( ( ( f != '' ) ? ', ' : ' ' ) + availcommands . shift ( ) ) ;
2019-09-27 13:30:00 -04:00
}
if ( f != '' ) { fin += f ; }
2020-01-07 16:04:36 -05:00
response = "Available commands: \r\n" + fin + "." ;
2018-04-19 21:19:15 -04:00
break ;
}
2020-07-31 21:05:00 -04:00
case 'agentmsg' : {
if ( args [ '_' ] . length == 0 ) {
response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list" ; // Display usage
2020-07-28 21:45:17 -04:00
} else {
2020-07-31 21:05:00 -04:00
if ( ( args [ '_' ] [ 0 ] == 'add' ) && ( args [ '_' ] . length > 1 ) ) {
var msgIndex = 1 , iconIndex = 0 ;
while ( tunnelUserCount . msg [ msgIndex ] != null ) { msgIndex ++ ; }
2020-09-30 19:59:34 -04:00
if ( args [ '_' ] . length >= 3 ) { try { iconIndex = parseInt ( args [ '_' ] [ 2 ] ) ; } catch ( e ) { } }
2020-07-31 21:05:00 -04:00
if ( typeof iconIndex != 'number' ) { iconIndex = 0 ; }
tunnelUserCount . msg [ msgIndex ] = { msg : args [ '_' ] [ 1 ] , icon : iconIndex } ;
response = 'Agent message ' + msgIndex + ' added.' ;
} else if ( ( args [ '_' ] [ 0 ] == 'remove' ) && ( args [ '_' ] . length > 1 ) ) {
var msgIndex = 0 ;
2020-09-30 19:59:34 -04:00
try { msgIndex = parseInt ( args [ '_' ] [ 1 ] ) ; } catch ( x ) { }
2020-07-31 21:05:00 -04:00
if ( tunnelUserCount . msg [ msgIndex ] == null ) { response = "Message not found." ; } else { delete tunnelUserCount . msg [ msgIndex ] ; response = "Message removed." ; }
} else if ( args [ '_' ] [ 0 ] == 'list' ) {
response = JSON . stringify ( tunnelUserCount . msg , null , 2 ) ;
}
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'msg' , value : tunnelUserCount . msg } ) ; } catch ( x ) { }
2020-07-28 21:45:17 -04:00
}
break ;
}
case 'clearagentmsg' : {
tunnelUserCount . msg = { } ;
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'msg' , value : tunnelUserCount . msg } ) ; } catch ( x ) { }
2020-07-28 21:45:17 -04:00
break ;
}
2020-07-06 17:19:16 -04:00
case 'coredump' :
2020-07-06 19:01:44 -04:00
if ( args [ '_' ] . length != 1 ) {
2020-07-08 02:56:08 -04:00
response = "Proper usage: coredump on|off|status|clear" ; // Display usage
2020-07-06 19:01:44 -04:00
} else {
2020-07-06 17:19:16 -04:00
switch ( args [ '_' ] [ 0 ] . toLowerCase ( ) )
{
case 'on' :
2020-07-06 19:01:44 -04:00
process . coreDumpLocation = ( process . platform == 'win32' ) ? ( process . execPath . replace ( '.exe' , '.dmp' ) ) : ( process . execPath + '.dmp' ) ;
2020-07-06 17:19:16 -04:00
response = 'coredump is now on' ;
break ;
case 'off' :
process . coreDumpLocation = null ;
response = 'coredump is now off' ;
break ;
case 'status' :
2020-07-06 19:01:44 -04:00
response = 'coredump is: ' + ( ( process . coreDumpLocation == null ) ? 'off' : 'on' ) ;
2020-07-06 20:16:54 -04:00
if ( process . coreDumpLocation != null ) {
if ( process . platform == 'win32' ) {
if ( fs . existsSync ( process . coreDumpLocation ) ) { response += '\r\n CoreDump present at: ' + process . coreDumpLocation ; }
} else {
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) ) { response += '\r\n CoreDump present at: ' + process . cwd ( ) + 'core' ; }
}
}
2020-07-06 17:19:16 -04:00
break ;
2020-07-08 02:56:08 -04:00
case 'clear' :
db . Put ( 'CoreDumpTime' , null ) ;
response = 'coredump db cleared' ;
break ;
2020-07-06 17:19:16 -04:00
default :
2020-07-06 19:01:44 -04:00
response = "Proper usage: coredump on|off|status" ; // Display usage
2020-07-06 17:19:16 -04:00
break ;
}
}
break ;
2020-07-01 16:15:07 -04:00
case 'service' :
if ( args [ '_' ] . length != 1 )
{
2020-07-06 19:01:44 -04:00
response = "Proper usage: service status|restart" ; // Display usage
2020-07-01 16:15:07 -04:00
}
else
{
var s = require ( 'service-manager' ) . manager . getService ( process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ) ;
switch ( args [ '_' ] [ 0 ] . toLowerCase ( ) )
{
case 'status' :
response = 'Service ' + ( s . isRunning ( ) ? ( s . isMe ( ) ? '[SELF]' : '[RUNNING]' ) : ( '[NOT RUNNING]' ) ) ;
break ;
case 'restart' :
if ( s . isMe ( ) )
{
s . restart ( ) ;
}
else
{
response = 'Restarting another agent instance is not allowed' ;
}
break ;
default :
2020-07-06 19:01:44 -04:00
response = "Proper usage: service status|restart" ; // Display usage
2020-07-01 16:15:07 -04:00
break ;
}
if ( process . platform == 'win32' ) { s . close ( ) ; }
}
break ;
2020-06-24 19:04:52 -04:00
case 'zip' :
2020-08-13 16:13:21 -04:00
if ( args [ '_' ] . length == 0 ) {
2020-06-24 19:04:52 -04:00
response = "Proper usage: zip (output file name), input1 [, input n]" ; // Display usage
2020-08-13 16:13:21 -04:00
} else {
2020-06-24 19:04:52 -04:00
var p = args [ '_' ] . join ( ' ' ) . split ( ',' ) ;
var ofile = p . shift ( ) ;
sendConsoleText ( 'Writing ' + ofile + '...' ) ;
var out = require ( 'fs' ) . createWriteStream ( ofile , { flags : 'wb' } ) ;
out . fname = ofile ;
out . sessionid = sessionid ;
out . on ( 'close' , function ( ) { sendConsoleText ( 'DONE writing ' + this . fname , this . sessionid ) ; } ) ;
var zip = require ( 'zip-writer' ) . write ( { files : p } ) ;
zip . pipe ( out ) ;
}
break ;
case 'unzip' :
2020-08-13 16:13:21 -04:00
if ( args [ '_' ] . length == 0 ) {
2020-06-24 19:04:52 -04:00
response = "Proper usage: unzip input, destination" ; // Display usage
2020-08-13 16:13:21 -04:00
} else {
2020-06-24 19:04:52 -04:00
var p = args [ '_' ] . join ( ' ' ) . split ( ',' ) ;
2020-08-13 16:13:21 -04:00
if ( p . length != 2 ) { response = "Proper usage: unzip input, destination" ; break ; } // Display usage
2020-06-24 19:04:52 -04:00
var prom = require ( 'zip-reader' ) . read ( p [ 0 ] ) ;
prom . _dest = p [ 1 ] ;
prom . self = this ;
prom . sessionid = sessionid ;
2020-08-13 16:13:21 -04:00
prom . then ( function ( zipped ) {
2020-06-24 19:04:52 -04:00
sendConsoleText ( 'Extracting to ' + this . _dest + '...' , this . sessionid ) ;
zipped . extractAll ( this . _dest ) . then ( function ( ) { sendConsoleText ( 'finished unzipping' , this . sessionid ) ; } , function ( e ) { sendConsoleText ( 'Error unzipping: ' + e , this . sessionid ) ; } ) . parentPromise . sessionid = this . sessionid ;
} , function ( e ) { sendConsoleText ( 'Error unzipping: ' + e , this . sessionid ) ; } ) ;
}
break ;
2020-05-30 20:21:45 -04:00
case 'setbattery' :
// require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
if ( ( args [ '_' ] . length > 0 ) && ( ( args [ '_' ] [ 0 ] == 'ac' ) || ( args [ '_' ] [ 0 ] == 'dc' ) ) ) {
var b = { action : 'battery' , state : args [ '_' ] [ 0 ] } ;
if ( args [ '_' ] . length == 2 ) { b . level = parseInt ( args [ '_' ] [ 1 ] ) ; }
require ( 'MeshAgent' ) . SendCommand ( b ) ;
} else {
require ( 'MeshAgent' ) . SendCommand ( { action : 'battery' } ) ;
}
break ;
2020-05-19 17:56:11 -04:00
case 'fdsnapshot' :
require ( 'ChainViewer' ) . getSnapshot ( ) . then ( function ( c ) { sendConsoleText ( c , this . sessionid ) ; } ) . parentPromise . sessionid = sessionid ;
break ;
case 'fdcount' :
require ( 'DescriptorEvents' ) . getDescriptorCount ( ) . then (
function ( c )
{
sendConsoleText ( 'Descriptor Count: ' + c , this . sessionid ) ;
} , function ( e )
{
sendConsoleText ( 'Error fetching descriptor count: ' + e , this . sessionid ) ;
} ) . parentPromise . sessionid = sessionid ;
break ;
2020-05-17 03:22:26 -04:00
case 'uac' :
if ( process . platform != 'win32' )
{
response = 'Unknown command "uac", type "help" for list of avaialble commands.' ;
break ;
}
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: uac [get|interactive|secure]' ;
}
else
{
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) )
{
case 'GET' :
var secd = require ( 'win-registry' ) . QueryKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' , 'PromptOnSecureDesktop' ) ;
response = "UAC mode: " + ( secd == 0 ? "Interactive Desktop" : "Secure Desktop" ) ;
break ;
case 'INTERACTIVE' :
try
{
require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' , 'PromptOnSecureDesktop' , 0 ) ;
response = 'UAC mode changed to: Interactive Desktop' ;
}
catch ( e )
{
response = "Unable to change UAC Mode" ;
}
break ;
case 'SECURE' :
try
{
require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' , 'PromptOnSecureDesktop' , 1 ) ;
response = 'UAC mode changed to: Secure Desktop' ;
}
catch ( e )
{
response = "Unable to change UAC Mode" ;
}
break ;
default :
response = 'Proper usage: uac [get|interactive|secure]' ;
break ;
}
}
break ;
2020-04-24 19:53:58 -04:00
case 'vm' :
response = 'Virtual Machine = ' + require ( 'identifiers' ) . isVM ( ) ;
break ;
2020-04-08 20:09:11 -04:00
case 'startupoptions' :
response = JSON . stringify ( require ( 'MeshAgent' ) . getStartupOptions ( ) ) ;
break ;
2020-04-13 01:53:49 -04:00
case 'kvmmode' :
if ( require ( 'MeshAgent' ) . maxKvmTileSize == null )
{
response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands." ;
}
else
{
if ( require ( 'MeshAgent' ) . maxKvmTileSize == 0 )
{
response = 'KVM Mode: Full JUMBO' ;
}
else
{
response = 'KVM Mode: ' + ( require ( 'MeshAgent' ) . maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO' ) ;
response += ( ', TileLimit: ' + ( require ( 'MeshAgent' ) . maxKvmTileSize < 1024 ? ( require ( 'MeshAgent' ) . maxKvmTileSize + ' bytes' ) : ( Math . round ( require ( 'MeshAgent' ) . maxKvmTileSize / 1024 ) + ' Kbytes' ) ) ) ;
}
}
break ;
2020-03-12 00:25:03 -04:00
case 'alert' :
if ( args [ '_' ] . length == 0 )
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]" ; // Display usage
}
else
{
var p = args [ '_' ] . join ( ' ' ) . split ( ',' ) ;
if ( p . length < 2 )
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]" ; // Display usage
}
else
{
2020-03-19 17:10:31 -04:00
this . _alert = require ( 'message-box' ) . create ( p [ 0 ] , p [ 1 ] , p . length == 3 ? parseInt ( p [ 2 ] ) : 9999 , 1 ) ;
2020-03-12 00:25:03 -04:00
}
}
break ;
2020-01-07 12:51:15 -05:00
case 'agentsize' :
2020-01-07 14:08:32 -05:00
var actualSize = Math . floor ( require ( 'fs' ) . statSync ( process . execPath ) . size / 1024 ) ;
2020-01-07 16:04:36 -05:00
if ( process . platform == 'win32' ) {
2020-01-07 12:51:15 -05:00
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2020-01-07 16:04:36 -05:00
var writtenSize = 0 ;
2020-09-30 19:59:34 -04:00
try { writtenSize = require ( 'win-registry' ) . QueryKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' ) ; } catch ( e ) { response = e ; }
2020-01-07 16:04:36 -05:00
if ( writtenSize != actualSize ) {
response = "Size updated from: " + writtenSize + " to: " + actualSize ;
2020-09-30 19:59:34 -04:00
try { require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' , actualSize ) ; } catch ( e ) { response = e ; }
2020-01-07 16:04:36 -05:00
} else { response = "Agent Size: " + actualSize + " kb" ; }
} else { response = "Agent Size: " + actualSize + " kb" ; }
2020-01-07 12:51:15 -05:00
break ;
2020-04-21 22:33:53 -04:00
case 'versions' :
response = JSON . stringify ( process . versions , null , ' ' ) ;
break ;
2019-12-10 17:15:14 -05:00
case 'wpfhwacceleration' :
2020-01-07 16:04:36 -05:00
if ( process . platform != 'win32' ) { throw ( "wpfhwacceleration setting is only supported on Windows" ) ; }
if ( args [ '_' ] . length != 1 ) {
response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)" ; // Display usage
2019-12-10 17:15:14 -05:00
}
2020-01-07 16:04:36 -05:00
else {
2019-12-10 17:15:14 -05:00
var reg = require ( 'win-registry' ) ;
var uname = require ( 'user-sessions' ) . getUsername ( require ( 'user-sessions' ) . consoleUid ( ) ) ;
var key = reg . usernameToUserKey ( uname ) ;
2020-01-07 16:04:36 -05:00
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) ) {
2019-12-10 17:15:14 -05:00
default :
2020-01-07 16:04:36 -05:00
response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)" ; // Display usage
2019-12-10 17:15:14 -05:00
break ;
case 'ON' :
2020-01-07 16:04:36 -05:00
try {
2019-12-10 17:23:35 -05:00
reg . WriteKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' , 0 ) ;
2020-01-07 16:04:36 -05:00
response = "OK" ;
2020-09-30 19:59:34 -04:00
} catch ( e ) { response = "FAILED" ; }
2019-12-10 17:15:14 -05:00
break ;
case 'OFF' :
2020-01-07 16:04:36 -05:00
try {
2019-12-10 17:23:35 -05:00
reg . WriteKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' , 1 ) ;
2019-12-10 17:15:14 -05:00
response = 'OK' ;
2020-09-30 19:59:34 -04:00
} catch ( e ) { response = 'FAILED' ; }
2019-12-10 17:15:14 -05:00
break ;
case 'STATUS' :
var s ;
2020-09-30 19:59:34 -04:00
try { s = reg . QueryKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' ) == 1 ? 'DISABLED' : 'ENABLED' ; } catch ( e ) { s = 'DEFAULT' ; }
2020-01-07 16:04:36 -05:00
response = "WPF Hardware Acceleration: " + s ;
2019-12-10 17:15:14 -05:00
break ;
case 'DEFAULT' :
2020-09-30 19:59:34 -04:00
try { reg . DeleteKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' ) ; } catch ( e ) { }
2019-12-10 17:15:14 -05:00
response = 'OK' ;
break ;
}
}
break ;
2019-12-06 18:40:57 -05:00
case 'tsid' :
2020-01-07 16:04:36 -05:00
if ( process . platform == 'win32' ) {
if ( args [ '_' ] . length != 1 ) {
response = "TSID: " + ( require ( 'MeshAgent' ) . _tsid == null ? "console" : require ( 'MeshAgent' ) . _tsid ) ;
} else {
2019-12-06 18:40:57 -05:00
var i = parseInt ( args [ '_' ] [ 0 ] ) ;
require ( 'MeshAgent' ) . _tsid = ( isNaN ( i ) ? null : i ) ;
2020-01-07 16:04:36 -05:00
response = "TSID set to: " + ( require ( 'MeshAgent' ) . _tsid == null ? "console" : require ( 'MeshAgent' ) . _tsid ) ;
2019-12-06 18:40:57 -05:00
}
2020-01-07 16:04:36 -05:00
} else { response = "TSID command only supported on Windows" ; }
2019-12-06 18:40:57 -05:00
break ;
case 'activeusers' :
2020-01-07 16:04:36 -05:00
if ( process . platform == 'win32' ) {
2019-12-06 18:40:57 -05:00
var p = require ( 'user-sessions' ) . enumerateUsers ( ) ;
p . sessionid = sessionid ;
2020-01-07 16:04:36 -05:00
p . then ( function ( u ) {
2019-12-06 18:40:57 -05:00
var v = [ ] ;
2020-01-07 16:04:36 -05:00
for ( var i in u ) {
if ( u [ i ] . State == 'Active' ) { v . push ( { tsid : i , type : u [ i ] . StationName , user : u [ i ] . Username , domain : u [ i ] . Domain } ) ; }
2019-12-06 18:40:57 -05:00
}
sendConsoleText ( JSON . stringify ( v , null , 1 ) , this . sessionid ) ;
} ) ;
2020-01-07 16:04:36 -05:00
} else { response = "activeusers command only supported on Windows" ; }
2019-12-06 18:40:57 -05:00
break ;
2019-11-13 17:45:16 -05:00
case 'wallpaper' :
2020-01-07 16:04:36 -05:00
if ( process . platform != 'win32' && ! ( process . platform == 'linux' && require ( 'linux-gnome-helpers' ) . available ) ) {
response = "wallpaper command not supported on this platform" ;
2019-11-13 17:45:16 -05:00
}
2020-01-07 16:04:36 -05:00
else {
if ( args [ '_' ] . length != 1 ) {
2019-11-13 17:45:16 -05:00
response = 'Proper usage: wallpaper (GET|TOGGLE)' ; // Display usage
}
2020-01-07 16:04:36 -05:00
else {
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) ) {
2019-11-13 17:45:16 -05:00
default :
response = 'Proper usage: wallpaper (GET|TOGGLE)' ; // Display usage
break ;
case 'GET' :
case 'TOGGLE' :
2020-01-07 16:04:36 -05:00
if ( process . platform == 'win32' ) {
2019-11-14 18:22:00 -05:00
var id = require ( 'user-sessions' ) . getProcessOwnerName ( process . pid ) . tsid == 0 ? 1 : 0 ;
var child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' ] , { type : id } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
var current = child . stdout . str . trim ( ) ;
2019-11-28 15:27:44 -05:00
if ( args [ '_' ] [ 0 ] . toUpperCase ( ) == 'GET' ) {
2019-11-14 18:22:00 -05:00
response = current ;
break ;
}
2019-11-28 15:27:44 -05:00
if ( current != '' ) {
2019-11-14 18:22:00 -05:00
require ( 'MeshAgent' ) . _wallpaper = current ;
response = 'Wallpaper cleared' ;
2019-11-28 15:27:44 -05:00
} else {
2019-11-14 18:22:00 -05:00
response = 'Wallpaper restored' ;
}
child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' , current != '' ? '""' : require ( 'MeshAgent' ) . _wallpaper ] , { type : id } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
2019-11-13 17:45:16 -05:00
}
2020-01-07 16:04:36 -05:00
else {
2019-11-14 18:22:00 -05:00
var id = require ( 'user-sessions' ) . consoleUid ( ) ;
var current = require ( 'linux-gnome-helpers' ) . getDesktopWallpaper ( id ) ;
2019-11-28 15:27:44 -05:00
if ( args [ '_' ] [ 0 ] . toUpperCase ( ) == 'GET' ) {
2019-11-14 18:22:00 -05:00
response = current ;
break ;
}
2019-11-28 15:27:44 -05:00
if ( current != '/dev/null' ) {
2019-11-14 18:22:00 -05:00
require ( 'MeshAgent' ) . _wallpaper = current ;
response = 'Wallpaper cleared' ;
2019-11-28 15:27:44 -05:00
} else {
2019-11-14 18:22:00 -05:00
response = 'Wallpaper restored' ;
}
require ( 'linux-gnome-helpers' ) . setDesktopWallpaper ( id , current != '/dev/null' ? undefined : require ( 'MeshAgent' ) . _wallpaper ) ;
2019-11-13 17:45:16 -05:00
}
break ;
}
}
}
break ;
2019-10-25 13:33:10 -04:00
case 'safemode' :
2020-01-07 16:04:36 -05:00
if ( process . platform != 'win32' ) {
2019-10-25 13:33:10 -04:00
response = 'safemode only supported on Windows Platforms'
}
2020-01-07 16:04:36 -05:00
else {
if ( args [ '_' ] . length != 1 ) {
2019-10-25 13:33:10 -04:00
response = 'Proper usage: safemode (ON|OFF|STATUS)' ; // Display usage
2020-01-07 16:04:36 -05:00
}
else {
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) ) {
2019-10-25 13:33:10 -04:00
default :
response = 'Proper usage: safemode (ON|OFF|STATUS)' ; // Display usage
break ;
case 'ON' :
require ( 'win-bcd' ) . setKey ( 'safeboot' , 'Network' ) ;
require ( 'win-bcd' ) . enableSafeModeService ( 'Mesh Agent' ) ;
break ;
case 'OFF' :
require ( 'win-bcd' ) . deleteKey ( 'safeboot' ) ;
break ;
case 'STATUS' :
var nextboot = require ( 'win-bcd' ) . getKey ( 'safeboot' ) ;
2020-01-07 16:04:36 -05:00
if ( nextboot ) {
switch ( nextboot ) {
2019-10-29 14:05:33 -04:00
case 'Network' :
case 'network' :
nextboot = 'SAFE_MODE_NETWORK' ;
break ;
default :
nextboot = 'SAFE_MODE' ;
break ;
}
}
2019-11-28 15:27:44 -05:00
response = 'Current: ' + require ( 'win-bcd' ) . bootMode + ', NextBoot: ' + ( nextboot ? nextboot : 'NORMAL' ) ;
2019-10-25 13:33:10 -04:00
break ;
}
}
}
break ;
2019-08-02 14:35:59 -04:00
/ *
2018-08-29 21:47:22 -04:00
case 'border' :
{
if ( ( args [ '_' ] . length == 1 ) && ( args [ '_' ] [ 0 ] == 'on' ) ) {
2018-11-25 15:59:42 -05:00
if ( meshCoreObj . users . length > 0 ) {
obj . borderManager . Start ( meshCoreObj . users [ 0 ] ) ;
2018-08-29 21:47:22 -04:00
response = 'Border blinking is on.' ;
} else {
response = 'Cannot turn on border blinking, no logged in users.' ;
}
} else if ( ( args [ '_' ] . length == 1 ) && ( args [ '_' ] [ 0 ] == 'off' ) ) {
obj . borderManager . Stop ( ) ;
response = 'Border blinking is off.' ;
} else {
response = 'Proper usage: border "on|off"' ; // Display correct command usage
}
}
break ;
2019-08-02 14:35:59 -04:00
* /
case 'av' :
2019-08-06 20:58:29 -04:00
if ( process . platform == 'win32' ) {
2019-10-11 16:45:23 -04:00
// Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
2019-08-06 20:58:29 -04:00
response = JSON . stringify ( require ( 'win-info' ) . av ( ) , null , 1 ) ;
} else {
response = 'Not supported on the platform' ;
}
2019-08-02 14:35:59 -04:00
break ;
2019-07-29 19:35:48 -04:00
case 'log' :
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: log "sample text"' ; } else { MeshServerLog ( args [ '_' ] [ 0 ] ) ; response = 'ok' ; }
break ;
2019-03-17 22:34:52 -04:00
case 'getclip' :
if ( require ( 'MeshAgent' ) . isService ) {
require ( 'clipboard' ) . dispatchRead ( ) . then ( function ( str ) { sendConsoleText ( str , sessionid ) ; } ) ;
2019-03-19 12:54:52 -04:00
} else {
2019-03-17 22:34:52 -04:00
require ( "clipboard" ) . read ( ) . then ( function ( str ) { sendConsoleText ( str , sessionid ) ; } ) ;
}
2019-02-17 00:16:39 -05:00
break ;
case 'setclip' : {
2019-03-17 22:34:52 -04:00
if ( args [ '_' ] . length != 1 ) {
2019-03-19 12:54:52 -04:00
response = 'Proper usage: setclip "sample text"' ;
} else {
2019-03-17 22:34:52 -04:00
if ( require ( 'MeshAgent' ) . isService ) {
require ( 'clipboard' ) . dispatchWrite ( args [ '_' ] [ 0 ] ) ;
2019-03-19 12:54:52 -04:00
response = 'Setting clipboard to: "' + args [ '_' ] [ 0 ] + '"' ;
2019-03-17 22:34:52 -04:00
}
else {
2019-03-19 12:54:52 -04:00
require ( "clipboard" ) ( args [ '_' ] [ 0 ] ) ; response = 'Setting clipboard to: "' + args [ '_' ] [ 0 ] + '"' ;
2019-03-17 22:34:52 -04:00
}
}
2019-02-17 00:16:39 -05:00
break ;
}
2018-12-28 00:31:20 -05:00
case 'amtreset' : {
2019-03-05 02:48:45 -05:00
if ( amt != null ) { amt . reset ( ) ; response = 'Done.' ; }
break ;
}
case 'amtlmsreset' : {
if ( amt != null ) { amt . lmsreset ( ) ; response = 'Done.' ; }
2018-12-28 00:31:20 -05:00
break ;
}
case 'amtccm' : {
2019-03-05 02:48:45 -05:00
if ( amt == null ) { response = 'Intel AMT not supported.' ; } else {
2018-12-28 00:31:20 -05:00
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: amtccm (adminPassword)' ; } // Display usage
2019-03-05 02:48:45 -05:00
else { amt . setPolicy ( { type : 0 } ) ; amt . activeToCCM ( args [ '_' ] [ 0 ] ) ; }
2018-12-28 00:31:20 -05:00
}
break ;
}
2019-06-14 19:33:53 -04:00
case 'amtacm' : {
if ( amt == null ) { response = 'Intel AMT not supported.' ; } else {
amt . setPolicy ( { type : 0 } ) ;
amt . getAmtInfo ( function ( meinfo ) { amt . activeToACM ( meinfo ) ; } ) ;
}
break ;
}
2018-12-28 00:31:20 -05:00
case 'amtdeactivate' : {
2019-03-05 02:48:45 -05:00
if ( amt == null ) { response = 'Intel AMT not supported.' ; } else { amt . setPolicy ( { type : 0 } ) ; amt . deactivateCCM ( ) ; }
2018-12-28 00:31:20 -05:00
break ;
}
2019-02-07 18:00:10 -05:00
case 'amtpolicy' : {
if ( amtPolicy == null ) {
response = 'No Intel(R) AMT policy.' ;
} else {
response = JSON . stringify ( amtPolicy ) ;
}
break ;
}
2018-12-07 19:36:27 -05:00
case 'openurl' : {
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: openurl (url)' ; } // Display usage
2018-12-14 13:19:25 -05:00
else { if ( openUserDesktopUrl ( args [ '_' ] [ 0 ] ) == null ) { response = 'Failed.' ; } else { response = 'Success.' ; } }
2018-12-07 19:36:27 -05:00
break ;
}
2018-11-25 15:59:42 -05:00
case 'users' : {
if ( meshCoreObj . users == null ) { response = 'Active users are unknown.' ; } else { response = 'Active Users: ' + meshCoreObj . users . join ( ', ' ) + '.' ; }
2018-12-20 15:12:24 -05:00
require ( 'user-sessions' ) . enumerateUsers ( ) . then ( function ( u ) { for ( var i in u ) { sendConsoleText ( u [ i ] ) ; } } ) ;
2018-08-29 21:47:22 -04:00
break ;
2018-11-25 15:59:42 -05:00
}
2018-04-19 21:19:15 -04:00
case 'toast' : {
2020-01-07 16:04:36 -05:00
if ( args [ '_' ] . length < 1 ) { response = 'Proper usage: toast "message"' ; } else {
if ( require ( 'MeshAgent' ) . _tsid == null ) {
2019-12-10 13:24:10 -05:00
require ( 'toaster' ) . Toast ( 'MeshCentral' , args [ '_' ] [ 0 ] ) . then ( sendConsoleText , sendConsoleText ) ;
}
2020-01-07 16:04:36 -05:00
else {
2019-12-10 13:24:10 -05:00
require ( 'toaster' ) . Toast ( 'MeshCentral' , args [ '_' ] [ 0 ] , require ( 'MeshAgent' ) . _tsid ) . then ( sendConsoleText , sendConsoleText ) ;
}
2018-04-20 19:05:06 -04:00
}
2018-03-08 20:58:22 -05:00
break ;
}
case 'setdebug' : {
2018-08-29 21:47:22 -04:00
if ( args [ '_' ] . length < 1 ) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile' ; } // Display usage
else { if ( args [ '_' ] [ 0 ] == '*' ) { console . setDestination ( 2 ) ; } else { console . setDestination ( parseInt ( args [ '_' ] [ 0 ] ) , sessionid ) ; } }
2018-01-12 14:41:26 -05:00
break ;
}
2018-04-11 16:49:05 -04:00
case 'ps' : {
2020-01-25 06:08:01 -05:00
processManager . getProcesses ( function ( plist ) {
2018-04-11 16:49:05 -04:00
var x = '' ;
2020-01-10 18:04:03 -05:00
for ( var i in plist ) { x += i + ( ( plist [ i ] . user ) ? ( ', ' + plist [ i ] . user ) : '' ) + ', ' + plist [ i ] . cmd + '\r\n' ; }
2018-04-11 16:49:05 -04:00
sendConsoleText ( x , sessionid ) ;
} ) ;
break ;
}
case 'kill' : {
if ( ( args [ '_' ] . length < 1 ) ) {
response = 'Proper usage: kill [pid]' ; // Display correct command usage
} else {
process . kill ( parseInt ( args [ '_' ] [ 0 ] ) ) ;
response = 'Killed process ' + args [ '_' ] [ 0 ] + '.' ;
}
break ;
}
2018-03-12 21:16:06 -04:00
case 'smbios' : {
2018-09-27 19:17:05 -04:00
if ( SMBiosTables == null ) { response = 'SMBios tables not available.' ; } else { response = objToString ( SMBiosTables , 0 , ' ' , true ) ; }
2018-03-12 21:16:06 -04:00
break ;
}
case 'rawsmbios' : {
2018-09-27 19:17:05 -04:00
if ( SMBiosTablesRaw == null ) { response = 'SMBios tables not available.' ; } else {
response = '' ;
for ( var i in SMBiosTablesRaw ) {
var header = false ;
for ( var j in SMBiosTablesRaw [ i ] ) {
if ( SMBiosTablesRaw [ i ] [ j ] . length > 0 ) {
if ( header == false ) { response += ( 'Table type #' + i + ( ( require ( 'smbios' ) . smTableTypes [ i ] == null ) ? '' : ( ', ' + require ( 'smbios' ) . smTableTypes [ i ] ) ) ) + '\r\n' ; header = true ; }
response += ( ' ' + SMBiosTablesRaw [ i ] [ j ] . toString ( 'hex' ) ) + '\r\n' ;
2018-03-12 21:16:06 -04:00
}
}
2018-09-27 19:17:05 -04:00
}
}
2018-03-12 21:16:06 -04:00
break ;
}
2018-01-12 14:41:26 -05:00
case 'eval' : { // Eval JavaScript
if ( args [ '_' ] . length < 1 ) {
response = 'Proper usage: eval "JavaScript code"' ; // Display correct command usage
} else {
2020-09-22 17:25:22 -04:00
response = JSON . stringify ( mesh . eval ( args [ '_' ] [ 0 ] ) ) ; // This can only be run by trusted administrator.
2018-01-12 14:41:26 -05:00
}
2017-08-28 12:27:45 -04:00
break ;
}
2019-10-24 16:13:18 -04:00
case 'uninstallagent' : // Uninstall this agent
2019-09-16 14:11:53 -04:00
var agentName = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
2019-10-10 14:13:25 -04:00
if ( ! require ( 'service-manager' ) . manager . getService ( agentName ) . isMe ( ) ) {
2019-09-16 14:11:53 -04:00
response = 'Uininstall failed, this instance is not the service instance' ;
2019-10-24 16:13:18 -04:00
} else {
2020-09-30 19:59:34 -04:00
try { diagnosticAgent _uninstall ( ) ; } catch ( e ) { }
2019-09-16 14:11:53 -04:00
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();" ;
this . child = require ( 'child_process' ) . execFile ( process . execPath , [ process . platform == 'win32' ? ( process . execPath . split ( '\\' ) . pop ( ) ) : ( process . execPath . split ( '/' ) . pop ( ) ) , '-b64exec' , Buffer . from ( js ) . toString ( 'base64' ) ] , { type : 4 , detached : true } ) ;
}
break ;
2017-08-28 12:27:45 -04:00
case 'notify' : { // Send a notification message to the mesh
if ( args [ '_' ] . length != 1 ) {
response = 'Proper usage: notify "message" [--session]' ; // Display correct command usage
} else {
2020-01-07 16:04:36 -05:00
var notification = { action : 'msg' , type : 'notify' , value : args [ '_' ] [ 0 ] , tag : 'console' } ;
2017-08-28 12:27:45 -04:00
if ( args . session ) { notification . sessionid = sessionid ; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
mesh . SendCommand ( notification ) ; // no sessionid or userid specified, notification will go to the entire mesh
2020-01-07 16:04:36 -05:00
response = "ok" ;
2017-08-28 12:27:45 -04:00
}
break ;
}
2019-08-06 20:58:29 -04:00
case 'cpuinfo' : { // Return system information
// CPU & memory utilization
pr = require ( 'sysinfo' ) . cpuUtilization ( ) ;
pr . sessionid = sessionid ;
pr . then ( function ( data ) {
sendConsoleText ( JSON . stringify ( { cpu : data , memory : require ( 'sysinfo' ) . memUtilization ( ) } , null , 1 ) , this . sessionid ) ;
} , function ( e ) {
sendConsoleText ( e ) ;
} ) ;
break ;
}
case 'sysinfo' : { // Return system information
2019-09-23 19:46:26 -04:00
getSystemInformation ( function ( results , err ) {
2019-09-24 13:34:33 -04:00
if ( results == null ) { sendConsoleText ( err , this . sessionid ) ; } else {
sendConsoleText ( JSON . stringify ( results , null , 1 ) , this . sessionid ) ;
}
2019-09-23 19:46:26 -04:00
} ) ;
2019-08-06 20:58:29 -04:00
break ;
}
2017-08-28 12:27:45 -04:00
case 'info' : { // Return information about the agent and agent core module
2020-04-08 22:16:33 -04:00
response = 'Current Core: ' + meshCoreObj . value + '\r\nAgent Time: ' + Date ( ) + '.\r\nUser Rights: 0x' + rights . toString ( 16 ) + '.\r\nPlatform: ' + process . platform + '.\r\nCapabilities: ' + meshCoreObj . caps + '.\r\nServer URL: ' + mesh . ServerUrl + '.' ;
2019-03-05 02:48:45 -05:00
if ( amt != null ) { response += '\r\nBuilt-in LMS: ' + [ 'Disabled' , 'Connecting..' , 'Connected' ] [ amt . lmsstate ] + '.' ; }
2018-11-25 15:59:42 -05:00
if ( meshCoreObj . osdesc ) { response += '\r\nOS: ' + meshCoreObj . osdesc + '.' ; }
2018-09-18 22:41:59 -04:00
response += '\r\nModules: ' + addedModules . join ( ', ' ) + '.' ;
2018-09-20 14:45:12 -04:00
response += '\r\nServer Connection: ' + mesh . isControlChannelConnected + ', State: ' + meshServerConnectionState + '.' ;
2018-09-21 19:34:35 -04:00
response += '\r\lastMeInfo: ' + lastMeInfo + '.' ;
2017-11-07 20:05:22 -05:00
var oldNodeId = db . Get ( 'OldNodeId' ) ;
2017-12-12 19:04:54 -05:00
if ( oldNodeId != null ) { response += '\r\nOldNodeID: ' + oldNodeId + '.' ; }
2019-06-21 18:40:56 -04:00
if ( process . platform == 'linux' || process . platform == 'freebsd' ) { response += '\r\nX11 support: ' + require ( 'monitor-info' ) . kvm _x11 _support + '.' ; }
2018-09-20 14:45:12 -04:00
break ;
}
case 'osinfo' : { // Return the operating system information
var i = 1 ;
2018-09-21 19:34:35 -04:00
if ( args [ '_' ] . length > 0 ) { i = parseInt ( args [ '_' ] [ 0 ] ) ; if ( i > 8 ) { i = 8 ; } response = 'Calling ' + i + ' times.' ; }
2020-01-07 16:04:36 -05:00
for ( var j = 0 ; j < i ; j ++ ) {
2018-09-20 14:45:12 -04:00
var pr = require ( 'os' ) . name ( ) ;
pr . sessionid = sessionid ;
2020-01-07 16:04:36 -05:00
pr . then ( function ( v ) {
sendConsoleText ( "OS: " + v + ( process . platform == 'win32' ? ( require ( 'win-virtual-terminal' ) . supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]' ) : '' ) , this . sessionid ) ;
2019-12-10 13:24:10 -05:00
} ) ;
2018-09-18 22:41:59 -04:00
}
2017-08-28 12:27:45 -04:00
break ;
}
2018-09-20 14:45:12 -04:00
case 'sendcaps' : { // Send capability flags to the server
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: sendcaps (number)' ; // Display correct command usage
} else {
2018-11-25 15:59:42 -05:00
meshCoreObj . caps = parseInt ( args [ '_' ] [ 0 ] ) ;
mesh . SendCommand ( meshCoreObj ) ;
response = JSON . stringify ( meshCoreObj ) ;
2018-09-20 14:45:12 -04:00
}
break ;
}
2018-09-21 19:34:35 -04:00
case 'sendosdesc' : { // Send OS description
2018-11-25 15:59:42 -05:00
if ( args [ '_' ] . length > 0 ) {
meshCoreObj . osdesc = args [ '_' ] [ 0 ] ;
mesh . SendCommand ( meshCoreObj ) ;
response = JSON . stringify ( meshCoreObj ) ;
} else {
response = 'Proper usage: sendosdesc [os description]' ; // Display correct command usage
}
2018-09-21 19:34:35 -04:00
break ;
}
2017-08-28 12:27:45 -04:00
case 'args' : { // Displays parsed command arguments
2018-03-20 20:48:03 -04:00
response = 'args ' + objToString ( args , 0 , ' ' , true ) ;
2017-08-28 12:27:45 -04:00
break ;
}
case 'print' : { // Print a message on the mesh agent console, does nothing when running in the background
var r = [ ] ;
for ( var i in args [ '_' ] ) { r . push ( args [ '_' ] [ i ] ) ; }
console . log ( r . join ( ' ' ) ) ;
response = 'Message printed on agent console.' ;
break ;
}
case 'type' : { // Returns the content of a file
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: type (filepath) [maxlength]' ; // Display correct command usage
} else {
var max = 4096 ;
if ( ( args [ '_' ] . length > 1 ) && ( typeof args [ '_' ] [ 1 ] == 'number' ) ) { max = args [ '_' ] [ 1 ] ; }
if ( max > 4096 ) max = 4096 ;
2019-01-02 21:34:50 -05:00
var buf = Buffer . alloc ( max ) , fd = fs . openSync ( args [ '_' ] [ 0 ] , "r" ) , r = fs . readSync ( fd , buf , 0 , max ) ; // Read the file content
2017-08-28 12:27:45 -04:00
response = buf . toString ( ) ;
var i = response . indexOf ( '\n' ) ;
if ( ( i > 0 ) && ( response [ i - 1 ] != '\r' ) ) { response = response . split ( '\n' ) . join ( '\r\n' ) ; }
if ( r == max ) response += '...' ;
fs . closeSync ( fd ) ;
}
break ;
}
2017-09-25 14:00:57 -04:00
case 'dbkeys' : { // Return all data store keys
response = JSON . stringify ( db . Keys ) ;
break ;
}
2017-08-28 12:27:45 -04:00
case 'dbget' : { // Return the data store value for a given key
if ( db == null ) { response = 'Database not accessible.' ; break ; }
if ( args [ '_' ] . length != 1 ) {
response = 'Proper usage: dbget (key)' ; // Display the value for a given database key
} else {
response = db . Get ( args [ '_' ] [ 0 ] ) ;
}
break ;
}
case 'dbset' : { // Set a data store key and value pair
if ( db == null ) { response = 'Database not accessible.' ; break ; }
if ( args [ '_' ] . length != 2 ) {
response = 'Proper usage: dbset (key) (value)' ; // Set a database key
} else {
var r = db . Put ( args [ '_' ] [ 0 ] , args [ '_' ] [ 1 ] ) ;
response = 'Key set: ' + r ;
}
break ;
}
case 'dbcompact' : { // Compact the data store
if ( db == null ) { response = 'Database not accessible.' ; break ; }
var r = db . Compact ( ) ;
response = 'Database compacted: ' + r ;
break ;
}
case 'httpget' : {
if ( consoleHttpRequest != null ) {
response = 'HTTP operation already in progress.' ;
} else {
if ( args [ '_' ] . length != 1 ) {
response = 'Proper usage: httpget (url)' ;
} else {
2017-09-20 17:44:22 -04:00
var options = http . parseUri ( args [ '_' ] [ 0 ] ) ;
2017-08-28 12:27:45 -04:00
options . method = 'GET' ;
if ( options == null ) {
response = 'Invalid url.' ;
} else {
try { consoleHttpRequest = http . request ( options , consoleHttpResponse ) ; } catch ( e ) { response = 'Invalid HTTP GET request' ; }
consoleHttpRequest . sessionid = sessionid ;
if ( consoleHttpRequest != null ) {
consoleHttpRequest . end ( ) ;
2017-09-20 17:44:22 -04:00
response = 'HTTPGET ' + options . protocol + '//' + options . host + ':' + options . port + options . path ;
2017-08-28 12:27:45 -04:00
}
}
}
}
break ;
}
case 'wslist' : { // List all web sockets
response = '' ;
for ( var i in consoleWebSockets ) {
var httprequest = consoleWebSockets [ i ] ;
response += 'Websocket #' + i + ', ' + httprequest . url + '\r\n' ;
}
if ( response == '' ) { response = 'no websocket sessions.' ; }
break ;
}
case 'wsconnect' : { // Setup a web socket
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc' ; // Display correct command usage
} else {
var httprequest = null ;
2017-09-01 20:34:02 -04:00
try {
2020-06-17 13:28:54 -04:00
var options = http . parseUri ( args [ '_' ] [ 0 ] . split ( '$' ) . join ( '%24' ) . split ( '@' ) . join ( '%40' ) ) ; // Escape the $ and @ characters in the URL
2018-01-04 15:15:21 -05:00
options . rejectUnauthorized = 0 ;
httprequest = http . request ( options ) ;
2017-09-01 20:34:02 -04:00
} catch ( e ) { response = 'Invalid HTTP websocket request' ; }
2017-08-28 12:27:45 -04:00
if ( httprequest != null ) {
httprequest . upgrade = onWebSocketUpgrade ;
2020-01-10 14:19:23 -05:00
httprequest . on ( 'error' , function ( e ) { sendConsoleText ( "ERROR: Unable to connect to: " + this . url + ", " + JSON . stringify ( e ) ) ; } ) ;
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
var index = 1 ;
while ( consoleWebSockets [ index ] ) { index ++ ; }
httprequest . sessionid = sessionid ;
httprequest . index = index ;
httprequest . url = args [ '_' ] [ 0 ] ;
consoleWebSockets [ index ] = httprequest ;
response = 'New websocket session #' + index ;
}
}
break ;
}
case 'wssend' : { // Send data on a web socket
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: wssend (socketnumber)\r\n' ; // Display correct command usage
for ( var i in consoleWebSockets ) {
var httprequest = consoleWebSockets [ i ] ;
response += 'Websocket #' + i + ', ' + httprequest . url + '\r\n' ;
}
} else {
var i = parseInt ( args [ '_' ] [ 0 ] ) ;
var httprequest = consoleWebSockets [ i ] ;
if ( httprequest != undefined ) {
httprequest . s . write ( args [ '_' ] [ 1 ] ) ;
response = 'ok' ;
} else {
response = 'Invalid web socket number' ;
}
}
break ;
}
case 'wsclose' : { // Close a websocket
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: wsclose (socketnumber)' ; // Display correct command usage
} else {
var i = parseInt ( args [ '_' ] [ 0 ] ) ;
var httprequest = consoleWebSockets [ i ] ;
if ( httprequest != undefined ) {
2017-09-01 20:34:02 -04:00
if ( httprequest . s != null ) { httprequest . s . end ( ) ; } else { httprequest . end ( ) ; }
2017-08-28 12:27:45 -04:00
response = 'ok' ;
} else {
response = 'Invalid web socket number' ;
}
}
break ;
}
case 'tunnels' : { // Show the list of current tunnels
response = '' ;
for ( var i in tunnels ) { response += 'Tunnel #' + i + ', ' + tunnels [ i ] . url + '\r\n' ; }
if ( response == '' ) { response = 'No websocket sessions.' ; }
break ;
}
case 'ls' : { // Show list of files and folders
response = '' ;
var xpath = '*' ;
if ( args [ '_' ] . length > 0 ) { xpath = obj . path . join ( args [ '_' ] [ 0 ] , '*' ) ; }
response = 'List of ' + xpath + '\r\n' ;
var results = fs . readdirSync ( xpath ) ;
for ( var i = 0 ; i < results . length ; ++ i ) {
var stat = null , p = obj . path . join ( args [ '_' ] [ 0 ] , results [ i ] ) ;
try { stat = fs . statSync ( p ) ; } catch ( e ) { }
if ( ( stat == null ) || ( stat == undefined ) ) {
response += ( results [ i ] + "\r\n" ) ;
} else {
response += ( results [ i ] + " " + ( ( stat . isDirectory ( ) ) ? "(Folder)" : "(File)" ) + "\r\n" ) ;
2017-10-23 17:09:58 -04:00
}
2017-08-28 12:27:45 -04:00
}
break ;
}
2018-07-23 20:34:24 -04:00
case 'lsx' : { // Show list of files and folders
response = objToString ( getDirectoryInfo ( args [ '_' ] [ 0 ] ) , 0 , ' ' , true ) ;
break ;
}
case 'lock' : { // Lock the current user out of the desktop
if ( process . platform == 'win32' ) { var child = require ( 'child_process' ) ; child . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ '/c' , 'RunDll32.exe user32.dll,LockWorkStation' ] , { type : 1 } ) ; response = 'Ok' ; }
else { response = 'Not supported on the platform' ; }
break ;
}
2017-08-28 12:27:45 -04:00
case 'amt' : { // Show Intel AMT status
2019-03-05 02:48:45 -05:00
if ( amt != null ) {
amt . getAmtInfo ( function ( state ) {
var resp = 'Intel AMT not detected.' ;
if ( state != null ) { resp = objToString ( state , 0 , ' ' , true ) ; }
sendConsoleText ( resp , sessionid ) ;
} ) ;
2019-06-14 19:33:53 -04:00
} else {
response = 'Intel AMT not detected.' ;
2019-03-05 02:48:45 -05:00
}
2017-08-28 12:27:45 -04:00
break ;
}
case 'netinfo' : { // Show network interface information
2017-09-20 17:44:22 -04:00
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
2018-03-20 20:48:03 -04:00
response = objToString ( interfaces , 0 , ' ' , true ) ;
break ;
}
2017-09-20 17:44:22 -04:00
case 'wakeonlan' : { // Send wake-on-lan
if ( ( args [ '_' ] . length != 1 ) || ( args [ '_' ] [ 0 ] . length != 12 ) ) {
response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".' ;
} else {
var count = sendWakeOnLan ( args [ '_' ] [ 0 ] ) ;
response = 'Sent wake-on-lan on ' + count + ' interface(s).' ;
}
2017-08-28 12:27:45 -04:00
break ;
}
case 'sendall' : { // Send a message to all consoles on this mesh
sendConsoleText ( args [ '_' ] . join ( ' ' ) ) ;
break ;
}
2017-09-01 14:23:22 -04:00
case 'power' : { // Execute a power action on this computer
if ( mesh . ExecPowerState == undefined ) {
response = 'Power command not supported on this agent.' ;
} else {
2020-02-04 19:22:00 -05:00
if ( ( args [ '_' ] . length == 0 ) || isNaN ( Number ( args [ '_' ] [ 0 ] ) ) ) {
2017-09-01 14:23:22 -04:00
response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14' ; // Display correct command usage
} else {
2020-02-04 19:22:00 -05:00
var r = mesh . ExecPowerState ( Number ( args [ '_' ] [ 0 ] ) , Number ( args [ '_' ] [ 1 ] ) ) ;
2017-09-01 14:23:22 -04:00
response = 'Power action executed with return code: ' + r + '.' ;
}
}
2017-08-28 15:48:53 -04:00
break ;
}
2017-09-13 14:25:57 -04:00
case 'location' : {
getIpLocationData ( function ( location ) {
2020-01-07 16:04:36 -05:00
sendConsoleText ( objToString ( { action : 'iplocation' , type : 'publicip' , value : location } , 0 , ' ' ) ) ;
2017-09-13 14:25:57 -04:00
} ) ;
break ;
}
2017-09-20 17:44:22 -04:00
case 'parseuri' : {
response = JSON . stringify ( http . parseUri ( args [ '_' ] [ 0 ] ) ) ;
break ;
}
2017-10-04 23:35:52 -04:00
case 'scanwifi' : {
if ( wifiScanner != null ) {
var wifiPresent = wifiScanner . hasWireless ;
if ( wifiPresent ) { response = "Perfoming Wifi scan..." ; wifiScanner . Scan ( ) ; } else { response = "Wifi absent." ; }
} else { response = "Wifi module not present." ; }
2017-10-03 21:31:20 -04:00
break ;
}
2018-01-09 23:13:41 -05:00
case 'scanamt' : {
if ( amtscanner != null ) {
if ( args [ '_' ] . length != 1 ) {
response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n' ; // Display correct command usage
} else {
response = 'Scanning: ' + args [ '_' ] [ 0 ] + '...' ;
amtscanner . scan ( args [ '_' ] [ 0 ] , 2000 , function ( data ) {
if ( data . length > 0 ) {
var r = '' , pstates = [ 'NotActivated' , 'InActivation' , 'Activated' ] ;
for ( var i in data ) {
var x = data [ i ] ;
if ( r != '' ) { r += '\r\n' ; }
r += x . address + ' - Intel AMT v' + x . majorVersion + '.' + x . minorVersion ;
if ( x . provisioningState < 3 ) { r += ( ', ' + pstates [ x . provisioningState ] ) ; }
if ( x . provisioningState == 2 ) { r += ( ', ' + x . openPorts . join ( ', ' ) ) ; }
r += '.' ;
}
} else {
r = 'No Intel AMT found.' ;
}
sendConsoleText ( r ) ;
} ) ;
}
} else { response = "Intel AMT scanner module not present." ; }
break ;
}
case 'modules' : {
response = JSON . stringify ( addedModules ) ;
break ;
2019-02-07 23:06:01 -05:00
}
2019-07-31 19:49:23 -04:00
case 'listservices' : {
var services = require ( 'service-manager' ) . manager . enumerateService ( ) ;
response = JSON . stringify ( services , null , 1 ) ;
break ;
}
2019-02-07 23:06:01 -05:00
case 'getscript' : {
if ( args [ '_' ] . length != 1 ) {
2020-01-07 16:04:36 -05:00
response = "Proper usage: getscript [scriptNumber]." ;
2019-02-07 23:06:01 -05:00
} else {
2020-01-07 16:04:36 -05:00
mesh . SendCommand ( { action : 'getScript' , type : args [ '_' ] [ 0 ] } ) ;
2019-02-07 23:06:01 -05:00
}
break ;
2018-01-09 23:13:41 -05:00
}
2019-06-14 19:33:53 -04:00
case 'diagnostic' :
{
2019-10-10 14:13:25 -04:00
if ( ! mesh . DAIPC . listening ) {
2019-06-14 19:33:53 -04:00
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process . cwd ( ) + ') is not on a local file system' ;
break ;
}
var diag = diagnosticAgent _installCheck ( ) ;
2019-10-10 14:13:25 -04:00
if ( diag ) {
if ( args [ '_' ] . length == 1 && args [ '_' ] [ 0 ] == 'uninstall' ) {
2019-06-14 19:33:53 -04:00
diagnosticAgent _uninstall ( ) ;
response = 'Diagnostic Agent uninstalled' ;
}
2019-10-10 14:13:25 -04:00
else {
2019-06-14 19:33:53 -04:00
response = 'Diagnostic Agent installed at: ' + diag . appLocation ( ) ;
}
}
2019-10-10 14:13:25 -04:00
else {
if ( args [ '_' ] . length == 1 && args [ '_' ] [ 0 ] == 'install' ) {
2019-06-14 19:33:53 -04:00
diag = diagnosticAgent _installCheck ( true ) ;
2019-10-10 14:13:25 -04:00
if ( diag ) {
2019-06-14 19:33:53 -04:00
response = 'Diagnostic agent was installed at: ' + diag . appLocation ( ) ;
}
2019-10-10 14:13:25 -04:00
else {
2019-06-14 19:33:53 -04:00
response = 'Diagnostic agent installation failed' ;
}
}
2019-10-10 14:13:25 -04:00
else {
2019-06-14 19:33:53 -04:00
response = 'Diagnostic Agent Not installed. To install: diagnostic install' ;
}
}
if ( diag ) { diag . close ( ) ; diag = null ; }
break ;
}
2019-09-17 16:11:22 -04:00
case 'apf' : {
2019-10-10 14:13:25 -04:00
if ( meshCoreObj . intelamt !== null ) {
2019-09-17 16:11:22 -04:00
if ( args [ '_' ] . length == 1 ) {
if ( args [ '_' ] [ 0 ] == 'on' ) {
2020-01-07 16:04:36 -05:00
response = "Starting APF tunnel" ;
2019-09-17 16:11:22 -04:00
var apfarg = {
2019-10-10 14:13:25 -04:00
mpsurl : mesh . ServerUrl . replace ( 'agent.ashx' , 'apf.ashx' ) ,
mpsuser : Buffer . from ( mesh . ServerInfo . MeshID , 'hex' ) . toString ( 'base64' ) . substring ( 0 , 16 ) ,
mpspass : Buffer . from ( mesh . ServerInfo . MeshID , 'hex' ) . toString ( 'base64' ) . substring ( 0 , 16 ) ,
2019-09-17 16:11:22 -04:00
mpskeepalive : 60000 ,
clientname : require ( 'os' ) . hostname ( ) ,
clientaddress : '127.0.0.1' ,
2020-10-09 18:44:09 -04:00
clientuuid : meshCoreObj . intelamt . uuid ,
conntype : 2 // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
2019-09-17 16:11:22 -04:00
} ;
2020-10-09 18:44:09 -04:00
if ( ( apfarg . clientuuid == null ) || ( apfarg . clientuuid . length != 36 ) ) {
response = "Unable to get Intel AMT UUID: " + apfarg . clientuuid ;
} else {
var tobj = { debug : false } ;
apftunnel = require ( 'apfclient' ) ( tobj , apfarg ) ;
try {
apftunnel . connect ( ) ;
response += "...success" ;
} catch ( e ) {
response += JSON . stringify ( e ) ;
}
2019-10-10 14:13:25 -04:00
}
2019-09-17 16:11:22 -04:00
} else if ( args [ '_' ] [ 0 ] == 'off' ) {
2020-01-07 16:04:36 -05:00
response = "Stopping APF tunnel" ;
2019-09-17 16:11:22 -04:00
try {
apftunnel . disconnect ( ) ;
response += "..success" ;
} catch ( e ) {
response += JSON . stringify ( e ) ;
}
2019-10-10 14:13:25 -04:00
apftunnel = null ;
2019-09-17 16:11:22 -04:00
} else {
2020-01-07 16:04:36 -05:00
response = "Invalid command.\r\nCmd syntax: apf on|off" ;
2019-09-17 16:11:22 -04:00
}
2019-10-10 14:13:25 -04:00
} else {
2020-01-07 16:04:36 -05:00
response = "APF tunnel is " + ( apftunnel == null ? "off" : "on" ) ;
2019-09-17 16:11:22 -04:00
}
} else {
2020-01-07 16:04:36 -05:00
response = "APF tunnel requires Intel AMT" ;
2019-09-17 16:11:22 -04:00
}
break ;
}
2019-10-10 14:13:25 -04:00
case 'plugin' : {
if ( typeof args [ '_' ] [ 0 ] == 'string' ) {
try {
// Pass off the action to the plugin
// for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
// to control the output / actions here.
response = require ( args [ '_' ] [ 0 ] ) . consoleaction ( args , rights , sessionid , mesh ) ;
} catch ( e ) {
2020-01-07 16:04:36 -05:00
response = "There was an error in the plugin (" + e + ")" ;
2019-10-10 14:13:25 -04:00
}
} else {
2020-01-07 16:04:36 -05:00
response = "Proper usage: plugin [pluginName] [args]." ;
2019-10-10 14:13:25 -04:00
}
break ;
}
2017-08-28 12:27:45 -04:00
default : { // This is an unknown command, return an error message
2020-01-07 16:04:36 -05:00
response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands." ;
2017-08-28 12:27:45 -04:00
break ;
}
}
2020-01-07 16:04:36 -05:00
} catch ( e ) { response = "Command returned an exception error: " + e ; console . log ( e ) ; }
2017-08-28 12:27:45 -04:00
if ( response != null ) { sendConsoleText ( response , sessionid ) ; }
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Send a mesh agent console command
2017-08-28 15:48:53 -04:00
function sendConsoleText ( text , sessionid ) {
if ( typeof text == 'object' ) { text = JSON . stringify ( text ) ; }
2020-08-28 23:01:06 -04:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : text , sessionid : sessionid } ) ;
2017-08-28 15:48:53 -04:00
}
2019-06-20 19:56:19 -04:00
2017-08-28 12:27:45 -04:00
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Called when the server connection state changes
function handleServerConnection ( state ) {
meshServerConnectionState = state ;
if ( meshServerConnectionState == 0 ) {
// Server disconnected
if ( selfInfoUpdateTimer != null ) { clearInterval ( selfInfoUpdateTimer ) ; selfInfoUpdateTimer = null ; }
lastSelfInfo = null ;
} else {
// Server connected, send mesh core information
2017-11-07 20:05:22 -05:00
var oldNodeId = db . Get ( 'OldNodeId' ) ;
if ( oldNodeId != null ) { mesh . SendCommand ( { action : 'mc1migration' , oldnodeid : oldNodeId } ) ; }
2018-09-21 19:34:35 -04:00
2018-11-25 15:59:42 -05:00
// Update the server with basic info, logged in users and more.
mesh . SendCommand ( meshCoreObj ) ;
2018-09-21 19:34:35 -04:00
2018-09-27 19:17:05 -04:00
// Send SMBios tables if present
2020-01-07 16:04:36 -05:00
if ( SMBiosTablesRaw != null ) { mesh . SendCommand ( { action : 'smbios' , value : SMBiosTablesRaw } ) ; }
2018-09-27 19:17:05 -04:00
2018-09-21 19:34:35 -04:00
// Update the server on more advanced stuff, like Intel ME and Network Settings
meInfoStr = null ;
sendPeriodicServerUpdate ( ) ;
2019-08-02 14:35:59 -04:00
if ( selfInfoUpdateTimer == null ) { selfInfoUpdateTimer = setInterval ( sendPeriodicServerUpdate , 1200000 ) ; } // 20 minutes
2020-07-31 15:56:39 -04:00
// Send any state messages
if ( Object . keys ( tunnelUserCount . msg ) . length > 0 ) {
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'msg' , value : tunnelUserCount . msg } ) ; } catch ( e ) { }
2020-07-31 15:56:39 -04:00
}
2018-09-21 19:34:35 -04:00
}
2020-10-01 13:30:26 -04:00
// Send update to the registered application
if ( ( obj . DAIPC . _daipc != null ) && ( obj . DAIPC . _daipc . _registered != null ) ) {
if ( state == 1 ) {
var apps = { } ;
apps [ obj . DAIPC . _daipc . _registered ] = 1 ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'app' , value : apps } ) ; } catch ( e ) { }
}
obj . DAIPC . _daipc . _send ( { cmd : 'serverstate' , value : meshServerConnectionState , url : require ( 'MeshAgent' ) . ConnectedServer } ) ;
}
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-12-12 19:04:54 -05:00
// Update the server with the latest network interface information
var sendNetworkUpdateNagleTimer = null ;
function sendNetworkUpdateNagle ( ) { if ( sendNetworkUpdateNagleTimer != null ) { clearTimeout ( sendNetworkUpdateNagleTimer ) ; sendNetworkUpdateNagleTimer = null ; } sendNetworkUpdateNagleTimer = setTimeout ( sendNetworkUpdate , 5000 ) ; }
function sendNetworkUpdate ( force ) {
sendNetworkUpdateNagleTimer = null ;
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Update the network interfaces information data
2020-06-26 17:43:58 -04:00
var netInfo = { netif2 : require ( 'os' ) . networkInterfaces ( ) } ;
if ( netInfo . netif2 ) {
2019-05-08 19:18:44 -04:00
netInfo . action = 'netinfo' ;
var netInfoStr = JSON . stringify ( netInfo ) ;
if ( ( force == true ) || ( clearGatewayMac ( netInfoStr ) != clearGatewayMac ( lastNetworkInfo ) ) ) { mesh . SendCommand ( netInfo ) ; lastNetworkInfo = netInfoStr ; }
}
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-12-12 19:04:54 -05:00
// Called periodically to check if we need to send updates to the server
2018-09-21 19:34:35 -04:00
function sendPeriodicServerUpdate ( flags ) {
if ( meshServerConnectionState == 0 ) return ; // Not connected to server, do nothing.
if ( ! flags ) { flags = 0xFFFFFFFF ; }
2019-03-05 02:48:45 -05:00
if ( ( flags & 1 ) && ( amt != null ) ) {
2018-09-21 19:34:35 -04:00
// If we have a connected MEI, get Intel ME information
2019-03-05 02:48:45 -05:00
amt . getAmtInfo ( function ( meinfo ) {
2018-09-21 19:34:35 -04:00
try {
if ( meinfo == null ) return ;
2020-08-11 21:38:38 -04:00
var intelamt = { } ;
if ( amt != null )
{
switch ( amt . lmsstate )
{
2020-10-09 18:44:09 -04:00
case 0 : intelamt . microlms = 'DISABLED' ; break ;
case 1 : intelamt . microlms = 'CONNECTING' ; break ;
case 2 : intelamt . microlms = 'CONNECTED' ; break ;
default : intelamt . microlms = 'unknown' ; break ;
2020-08-11 21:38:38 -04:00
}
}
var p = false ;
2019-10-01 17:54:38 -04:00
if ( ( meinfo . Versions != null ) && ( meinfo . Versions . AMT != null ) ) { intelamt . ver = meinfo . Versions . AMT ; p = true ; if ( meinfo . Versions . Sku != null ) { intelamt . sku = parseInt ( meinfo . Versions . Sku ) ; } }
2019-06-13 19:39:21 -04:00
if ( meinfo . ProvisioningState != null ) { intelamt . state = meinfo . ProvisioningState ; p = true ; }
if ( meinfo . Flags != null ) { intelamt . flags = meinfo . Flags ; p = true ; }
if ( meinfo . OsHostname != null ) { intelamt . host = meinfo . OsHostname ; p = true ; }
if ( meinfo . UUID != null ) { intelamt . uuid = meinfo . UUID ; p = true ; }
if ( ( meinfo . ProvisioningState == 0 ) && ( meinfo . net0 != null ) && ( meinfo . net0 . enabled == 1 ) ) { // If not activated, look to see if we have wired net working.
// Not activated and we have wired ethernet, look for the trusted DNS
2019-06-14 19:33:53 -04:00
var dns = meinfo . DNS ;
2019-06-13 19:39:21 -04:00
if ( dns == null ) {
// Trusted DNS not set, let's look for the OS network DNS suffix
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
for ( var i in interfaces ) {
for ( var j in interfaces [ i ] ) {
if ( ( interfaces [ i ] [ j ] . mac == mestate . net0 . mac ) && ( interfaces [ i ] [ j ] . fqdn != null ) && ( interfaces [ i ] [ j ] . fqdn != '' ) ) { dns = interfaces [ i ] [ j ] . fqdn ; }
}
}
}
if ( intelamt . dns != dns ) { intelamt . dns = dns ; p = true ; }
} else { if ( intelamt . dns != null ) { delete intelamt . dns ; p = true ; } }
2018-09-21 19:34:35 -04:00
if ( p == true ) {
2018-11-25 15:59:42 -05:00
var meInfoStr = JSON . stringify ( intelamt ) ;
if ( meInfoStr != lastMeInfo ) {
meshCoreObj . intelamt = intelamt ;
mesh . SendCommand ( meshCoreObj ) ;
lastMeInfo = meInfoStr ;
}
2018-09-21 19:34:35 -04:00
}
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2018-02-11 20:13:26 -05:00
} ) ;
2017-12-12 19:04:54 -05:00
}
2018-09-21 19:34:35 -04:00
if ( flags & 2 ) {
// Update network information
sendNetworkUpdateNagle ( false ) ;
}
2019-08-02 14:35:59 -04:00
if ( ( flags & 4 ) && ( process . platform == 'win32' ) ) {
// Update anti-virus information
2019-10-11 16:45:23 -04:00
// Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
2019-08-06 20:58:29 -04:00
var av , pr ;
2020-09-30 19:59:34 -04:00
try { av = require ( 'win-info' ) . av ( ) ; } catch ( e ) { av = null ; } // Antivirus
//if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (e) { pr = null; } } // Pending reboot
2019-08-02 14:35:59 -04:00
if ( ( meshCoreObj . av == null ) || ( JSON . stringify ( meshCoreObj . av ) != JSON . stringify ( av ) ) ) { meshCoreObj . av = av ; mesh . SendCommand ( meshCoreObj ) ; }
}
2017-12-12 19:04:54 -05:00
}
2019-10-10 14:13:25 -04:00
2018-12-28 00:31:20 -05:00
// Starting function
obj . start = function ( ) {
// Setup the mesh agent event handlers
mesh . AddCommandHandler ( handleServerCommand ) ;
mesh . AddConnectHandler ( handleServerConnection ) ;
// Parse input arguments
//var args = parseArgs(process.argv);
//console.log(args);
2019-03-05 02:48:45 -05:00
//resetMicroLms();
2018-01-12 14:41:26 -05:00
2018-12-14 13:19:25 -05:00
// Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
2018-09-21 19:34:35 -04:00
try {
var userSession = require ( 'user-sessions' ) ;
userSession . on ( 'changed' , function onUserSessionChanged ( ) {
userSession . enumerateUsers ( ) . then ( function ( users ) {
var u = [ ] , a = users . Active ;
2018-09-24 20:47:03 -04:00
for ( var i = 0 ; i < a . length ; i ++ ) {
var un = a [ i ] . Domain ? ( a [ i ] . Domain + '\\' + a [ i ] . Username ) : ( a [ i ] . Username ) ;
if ( u . indexOf ( un ) == - 1 ) { u . push ( un ) ; } // Only push users in the list once.
}
2018-11-25 15:59:42 -05:00
meshCoreObj . users = u ;
mesh . SendCommand ( meshCoreObj ) ;
2018-09-21 19:34:35 -04:00
} ) ;
2018-08-29 21:47:22 -04:00
} ) ;
2018-09-21 19:34:35 -04:00
userSession . emit ( 'changed' ) ;
//userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
2020-09-30 19:59:34 -04:00
} catch ( e ) { }
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
obj . stop = function ( ) {
mesh . AddCommandHandler ( null ) ;
mesh . AddConnectHandler ( null ) ;
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
function onWebSocketClosed ( ) { sendConsoleText ( "WebSocket #" + this . httprequest . index + " closed." , this . httprequest . sessionid ) ; delete consoleWebSockets [ this . httprequest . index ] ; }
function onWebSocketData ( data ) { sendConsoleText ( "Got WebSocket #" + this . httprequest . index + " data: " + data , this . httprequest . sessionid ) ; }
function onWebSocketSendOk ( ) { sendConsoleText ( "WebSocket #" + this . index + " SendOK." , this . sessionid ) ; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
function onWebSocketUpgrade ( response , s , head ) {
sendConsoleText ( "WebSocket #" + this . index + " connected." , this . sessionid ) ;
this . s = s ;
s . httprequest = this ;
s . end = onWebSocketClosed ;
s . data = onWebSocketData ;
}
2018-07-02 17:34:10 -04:00
2017-08-28 12:27:45 -04:00
return obj ;
}
2018-07-02 17:34:10 -04:00
//
// Module startup
//
2018-09-20 14:45:12 -04:00
try {
var xexports = null , mainMeshCore = null ;
try { xexports = module . exports ; } catch ( e ) { }
2017-08-28 12:27:45 -04:00
2018-09-20 14:45:12 -04:00
if ( xexports != null ) {
// If we are running within NodeJS, export the core
module . exports . createMeshCore = createMeshCore ;
} else {
// If we are not running in NodeJS, launch the core
mainMeshCore = createMeshCore ( ) ;
mainMeshCore . start ( null ) ;
}
2020-09-30 19:59:34 -04:00
} catch ( e ) {
2020-01-07 16:04:36 -05:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : "uncaughtException2: " + ex } ) ;
2019-12-22 06:37:24 -05:00
}