mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-10-29 23:35:02 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1457 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1457 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| var http = require('http');
 | |
| var childProcess = require('child_process');
 | |
| var meshCoreObj = { action: 'coreinfo', value: "MeshCore Recovery", caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
 | |
| var nextTunnelIndex = 1;
 | |
| var tunnels = {};
 | |
| var fs = require('fs');
 | |
| 
 | |
| var needStreamFix = (new Date(process.versions.meshAgent) < new Date('2020-01-21 13:27:45.000-08:00'));
 | |
| try
 | |
| {
 | |
|     Object.defineProperty(Array.prototype, 'find', {
 | |
|         value: function (func)
 | |
|         {
 | |
|             var i = 0;
 | |
|             for(i=0;i<this.length;++i)
 | |
|             {
 | |
|                 if(func(this[i]))
 | |
|                 {
 | |
|                     return (this[i]);
 | |
|                 }
 | |
|             }
 | |
|             return (null);
 | |
|         }
 | |
|     });
 | |
| }
 | |
| catch(x)
 | |
| {
 | |
| 
 | |
| }
 | |
| try
 | |
| {
 | |
|     Object.defineProperty(Array.prototype, 'findIndex', {
 | |
|         value: function (func)
 | |
|         {
 | |
|             var i = 0;
 | |
|             for (i = 0; i < this.length; ++i)
 | |
|             {
 | |
|                 if (func(this[i], i, this))
 | |
|                 {
 | |
|                     return (i);
 | |
|                 }
 | |
|             }
 | |
|             return (-1);
 | |
|         }
 | |
|     });
 | |
| }
 | |
| catch (x)
 | |
| {
 | |
| 
 | |
| }
 | |
| 
 | |
| if (process.platform != 'win32')
 | |
| {
 | |
|     var ch = require('child_process');
 | |
|     ch._execFile = ch.execFile;
 | |
|     ch.execFile = function execFile(path, args, options)
 | |
|     {
 | |
|         if (options && options.type && options.type == ch.SpawnTypes.TERM && options.env)
 | |
|         {
 | |
|             options.env['TERM'] = 'xterm-256color';
 | |
|         }
 | |
|         return (this._execFile(path, args, options));
 | |
|     };
 | |
| }
 | |
| 
 | |
| function _getPotentialServiceNames()
 | |
| {
 | |
|     var registry = require('win-registry');
 | |
|     var ret = [];
 | |
|     var K = registry.QueryKey(registry.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services');
 | |
|     var service, s;
 | |
|     while (K.subkeys.length > 0)
 | |
|     {
 | |
|         service = K.subkeys.shift();
 | |
|         try
 | |
|         {
 | |
|             s = registry.QueryKey(registry.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services\\' + service, 'ImagePath');
 | |
|             if (s.startsWith(process.execPath) || s.startsWith('"' + process.execPath + '"'))
 | |
|             {
 | |
|                 ret.push(service);
 | |
|             }
 | |
|         }
 | |
|         catch (x)
 | |
|         {
 | |
|         }
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| function _verifyServiceName(names)
 | |
| {
 | |
|     var i;
 | |
|     var s;
 | |
|     var ret = null;
 | |
|     for (i = 0; i < names.length; ++i)
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             s = require('service-manager').manager.getService(names[i]);
 | |
|             if (s.isMe())
 | |
|             {
 | |
|                 ret = names[i];
 | |
|                 s.close();
 | |
|                 break;
 | |
|             }
 | |
|             s.close();
 | |
|         }
 | |
|         catch (z) { }
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| function windows_getCommandLine()
 | |
| {
 | |
|     var parms = [];
 | |
|     var GM = require('_GenericMarshal');
 | |
|     var k32 = GM.CreateNativeProxy('kernel32.dll');
 | |
|     var s32 = GM.CreateNativeProxy('shell32.dll');
 | |
|     k32.CreateMethod('GetCommandLineW');
 | |
|     k32.CreateMethod('LocalFree');
 | |
|     s32.CreateMethod('CommandLineToArgvW');
 | |
|     var v = k32.GetCommandLineW();
 | |
|     var i;
 | |
|     var len = GM.CreateVariable(4);
 | |
|     var val = s32.CommandLineToArgvW(v, len);
 | |
|     len = len.toBuffer().readInt32LE(0);
 | |
|     if (len > 0)
 | |
|     {
 | |
|         for (i = 0; i < len; ++i)
 | |
|         {
 | |
|             parms.push(val.Deref(i * GM.PointerSize, GM.PointerSize).Deref().Wide2UTF8);
 | |
|         }
 | |
|     }
 | |
|     k32.LocalFree(val);
 | |
|     return (parms);
 | |
| }
 | |
| 
 | |
| if (require('MeshAgent').ARCHID == null)
 | |
| {
 | |
|     var id = null;
 | |
|     switch (process.platform)
 | |
|     {
 | |
|         case 'win32':
 | |
|             id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4;
 | |
|             break;
 | |
|         case 'freebsd':
 | |
|             id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30;
 | |
|             break;
 | |
|         case 'darwin':
 | |
|             try
 | |
|             {
 | |
|                 id = require('os').arch() == 'x64' ? 16 : 29;
 | |
|             }
 | |
|             catch (xx)
 | |
|             {
 | |
|                 id = 16;
 | |
|             }
 | |
|             break;
 | |
|     }
 | |
|     if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); }
 | |
| }
 | |
| 
 | |
| //attachDebugger({ webport: 9994, wait: 1 }).then(function (p) { console.log('Debug on port: ' + p); });
 | |
| 
 | |
| function sendConsoleText(msg, sessionid)
 | |
| {
 | |
|     if (sessionid != null)
 | |
|     {
 | |
|         require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: msg, sessionid: sessionid });
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: msg });
 | |
|     }
 | |
| }
 | |
| 
 | |
| function sendAgentMessage(msg, icon)
 | |
| {
 | |
|     if (sendAgentMessage.messages == null)
 | |
|     {
 | |
|         sendAgentMessage.messages = {};
 | |
|         sendAgentMessage.nextid = 1;
 | |
|     }
 | |
|     sendAgentMessage.messages[sendAgentMessage.nextid++] = { msg: msg, icon: icon };
 | |
|     require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages });
 | |
| }
 | |
| 
 | |
| // Add to the server event log
 | |
| function MeshServerLog(msg, state)
 | |
| {
 | |
|     if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
 | |
|     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; }
 | |
|     }
 | |
|     require('MeshAgent').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; }
 | |
|     }
 | |
|     require('MeshAgent').SendCommand(msg);
 | |
| }
 | |
| 
 | |
| function getOpenDescriptors()
 | |
| {
 | |
|     switch(process.platform)
 | |
|     {
 | |
|         case "freebsd":
 | |
|             var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|             child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
 | |
|             child.stderr.on('data', function (c) { });
 | |
| 
 | |
|             child.stdin.write("procstat -f " + process.pid + " | tr '\\n' '`' | awk -F'`' '");
 | |
|             child.stdin.write('{');
 | |
|             child.stdin.write('   DEL="";');
 | |
|             child.stdin.write('   printf "[";');
 | |
|             child.stdin.write('   for(i=1;i<NF;++i)');
 | |
|             child.stdin.write('   {');
 | |
|             child.stdin.write('      A=split($i,B," ");');
 | |
|             child.stdin.write('      if(B[3] ~ /^[0-9]/)');
 | |
|             child.stdin.write('      {');
 | |
|             child.stdin.write('         printf "%s%s", DEL, B[3];');
 | |
|             child.stdin.write('         DEL=",";');
 | |
|             child.stdin.write('      }');
 | |
|             child.stdin.write('   }');
 | |
|             child.stdin.write('   printf "]";');
 | |
|             child.stdin.write("}'");
 | |
| 
 | |
|             child.stdin.write('\nexit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 return(JSON.parse(child.stdout.str.trim()));
 | |
|             }
 | |
|             catch(e)
 | |
|             {
 | |
|                 return ([]);
 | |
|             }
 | |
|             break;
 | |
|         case "linux":
 | |
|             var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|             child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
 | |
|             child.stderr.on('data', function (c) { });
 | |
| 
 | |
|             child.stdin.write("ls /proc/" + process.pid + "/fd | tr '\\n' '`' | awk -F'`' '");
 | |
|             child.stdin.write('{');
 | |
|             child.stdin.write('   printf "[";');
 | |
|             child.stdin.write('   DEL="";');
 | |
|             child.stdin.write('   for(i=1;i<NF;++i)');
 | |
|             child.stdin.write('   {');
 | |
|             child.stdin.write('      printf "%s%s",DEL,$i;');
 | |
|             child.stdin.write('      DEL=",";');
 | |
|             child.stdin.write('   }');
 | |
|             child.stdin.write('   printf "]";');
 | |
|             child.stdin.write("}'");
 | |
|             child.stdin.write('\nexit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 return (JSON.parse(child.stdout.str.trim()));
 | |
|             }
 | |
|             catch (e)
 | |
|             {
 | |
|                 return ([]);
 | |
|             }
 | |
|             break;
 | |
|         default:
 | |
|             return ([]);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| function pathjoin() {
 | |
|     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); }
 | |
|             if (i != 0) {
 | |
|                 while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
 | |
|             }
 | |
|             x.push(w);
 | |
|         }
 | |
|     }
 | |
|     if (x.length == 0) return '/';
 | |
|     return x.join('/');
 | |
| }
 | |
| // Replace a string with a number if the string is an exact number
 | |
| function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
 | |
| 
 | |
| 
 | |
| function closeDescriptors(libc, descriptors)
 | |
| {
 | |
|     var fd = null;
 | |
|     while(descriptors.length>0)
 | |
|     {
 | |
|         fd = descriptors.pop();
 | |
|         if(fd > 2)
 | |
|         {
 | |
|             libc.close(fd);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| function linux_execv(name, agentfilename, sessionid)
 | |
| {
 | |
|     var libs = require('monitor-info').getLibInfo('libc');
 | |
|     var libc = null;
 | |
| 
 | |
|     if ((libs.length == 0 || libs.length == null) && require('MeshAgent').ARCHID == 33)
 | |
|     {
 | |
|         var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|         child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
 | |
|         child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
 | |
|         child.stdin.write("ls /lib/libc.* | tr '\\n' '`' | awk -F'`' '{ " + ' printf "["; DEL=""; for(i=1;i<NF;++i) { printf "%s{\\"path\\":\\"%s\\"}",DEL,$i; DEL=""; } printf "]"; }\'\nexit\n');
 | |
|         child.waitExit();
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             libs = JSON.parse(child.stdout.str.trim());
 | |
|         }
 | |
|         catch(e)
 | |
|         {
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     while (libs.length > 0)
 | |
|     {
 | |
|         try {
 | |
|             libc = require('_GenericMarshal').CreateNativeProxy(libs.pop().path);
 | |
|             break;
 | |
|         }
 | |
|         catch (e) {
 | |
|             libc = null;
 | |
|             continue;
 | |
|         }
 | |
|     }
 | |
|     if (libc != null) {
 | |
|         try
 | |
|         {
 | |
|             libc.CreateMethod('execv');
 | |
|             libc.CreateMethod('close');
 | |
|         }
 | |
|         catch (e) {
 | |
|             libc = null;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (libc == null) {
 | |
|         // Couldn't find libc.so, fallback to using service manager to restart agent
 | |
|         if (sessionid != null) { sendConsoleText('Restarting service via service-manager...', sessionid) }
 | |
|         try {
 | |
|             // restart service
 | |
|             var s = require('service-manager').manager.getService(name);
 | |
|             s.restart();
 | |
|         }
 | |
|         catch (zz) {
 | |
|             sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
 | |
|             sendAgentMessage('Self Update encountered an error trying to restart service', 3);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (sessionid != null) { sendConsoleText('Restarting service via execv()...', sessionid) }
 | |
| 
 | |
|     var i;
 | |
|     var args;
 | |
|     var argtmp = [];
 | |
|     var argarr = [process.execPath];
 | |
|     var path = require('_GenericMarshal').CreateVariable(process.execPath);
 | |
| 
 | |
|     if (require('MeshAgent').getStartupOptions != null) {
 | |
|         var options = require('MeshAgent').getStartupOptions();
 | |
|         for (i in options) {
 | |
|             argarr.push('--' + i + '="' + options[i] + '"');
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     args = require('_GenericMarshal').CreateVariable((1 + argarr.length) * require('_GenericMarshal').PointerSize);
 | |
|     for (i = 0; i < argarr.length; ++i) {
 | |
|         var arg = require('_GenericMarshal').CreateVariable(argarr[i]);
 | |
|         argtmp.push(arg);
 | |
|         arg.pointerBuffer().copy(args.toBuffer(), i * require('_GenericMarshal').PointerSize);
 | |
|     }
 | |
| 
 | |
|     var descriptors = getOpenDescriptors();
 | |
|     closeDescriptors(libc, descriptors);
 | |
| 
 | |
|     libc.execv(path, args);
 | |
|     if (sessionid != null) { sendConsoleText('Self Update failed because execv() failed', sessionid) }
 | |
|     sendAgentMessage('Self Update failed because execv() failed', 3);
 | |
| }
 | |
| 
 | |
| function bsd_execv(name, agentfilename, sessionid) {
 | |
|     var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|     child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
 | |
|     child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
 | |
|     child.stdin.write("cat /usr/lib/libc.so | awk '");
 | |
|     child.stdin.write('{');
 | |
|     child.stdin.write(' a=split($0, tok, "(");');
 | |
|     child.stdin.write(' if(a>1)');
 | |
|     child.stdin.write(' {');
 | |
|     child.stdin.write('     split(tok[2], b, ")");');
 | |
|     child.stdin.write('     split(b[1], c, " ");');
 | |
|     child.stdin.write('     print c[1];');
 | |
|     child.stdin.write(' }');
 | |
|     child.stdin.write("}'\nexit\n");
 | |
|     child.waitExit();
 | |
|     if (child.stdout.str.trim() == '') {
 | |
|         if (sessionid != null) { sendConsoleText('Self Update failed because cannot find libc.so', sessionid) }
 | |
|         sendAgentMessage('Self Update failed because cannot find libc.so', 3);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     var libc = null;
 | |
|     try
 | |
|     {
 | |
|         libc = require('_GenericMarshal').CreateNativeProxy(child.stdout.str.trim());
 | |
|         libc.CreateMethod('execv');
 | |
|         libc.CreateMethod('close');
 | |
|     }
 | |
|     catch (e) {
 | |
|         if (sessionid != null) { sendConsoleText('Self Update failed: ' + e.toString(), sessionid) }
 | |
|         sendAgentMessage('Self Update failed: ' + e.toString(), 3);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     var i;
 | |
|     var path = require('_GenericMarshal').CreateVariable(process.execPath);
 | |
|     var argarr = [process.execPath];
 | |
|     var argtmp = [];
 | |
|     var args;
 | |
|     var options = require('MeshAgent').getStartupOptions();
 | |
|     for (i in options) {
 | |
|         argarr.push('--' + i + '="' + options[i] + '"');
 | |
|     }
 | |
|     args = require('_GenericMarshal').CreateVariable((1 + argarr.length) * require('_GenericMarshal').PointerSize);
 | |
|     for (i = 0; i < argarr.length; ++i) {
 | |
|         var arg = require('_GenericMarshal').CreateVariable(argarr[i]);
 | |
|         argtmp.push(arg);
 | |
|         arg.pointerBuffer().copy(args.toBuffer(), i * require('_GenericMarshal').PointerSize);
 | |
|     }
 | |
| 
 | |
|     if (sessionid != null) { sendConsoleText('Restarting service via execv()', sessionid) }
 | |
| 
 | |
|     var descriptors = getOpenDescriptors();
 | |
|     closeDescriptors(libc, descriptors);
 | |
| 
 | |
|     libc.execv(path, args);
 | |
|     if (sessionid != null) { sendConsoleText('Self Update failed because execv() failed', sessionid) }
 | |
|     sendAgentMessage('Self Update failed because execv() failed', 3);
 | |
| }
 | |
| 
 | |
| function windows_execve(name, agentfilename, sessionid) {
 | |
|     var libc;
 | |
|     try {
 | |
|         libc = require('_GenericMarshal').CreateNativeProxy('msvcrt.dll');
 | |
|         libc.CreateMethod('_wexecve');
 | |
|     }
 | |
|     catch (xx) {
 | |
|         sendConsoleText('Self Update failed because msvcrt.dll is missing', sessionid);
 | |
|         sendAgentMessage('Self Update failed because msvcrt.dll is missing', 3);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     var cwd = process.cwd();
 | |
|     if (!cwd.endsWith('\\'))
 | |
|     {
 | |
|         cwd += '\\';
 | |
|     }
 | |
|     var cmd = require('_GenericMarshal').CreateVariable(process.env['windir'] + '\\system32\\cmd.exe', { wide: true });
 | |
|     var args = require('_GenericMarshal').CreateVariable(3 * require('_GenericMarshal').PointerSize);
 | |
|     var arg1 = require('_GenericMarshal').CreateVariable('cmd.exe', { wide: true });
 | |
|     var arg2 = require('_GenericMarshal').CreateVariable('/C net stop "' + name + '" & "' + cwd + agentfilename + '.update.exe" -b64exec ' + 'dHJ5CnsKICAgIHZhciBzZXJ2aWNlTG9jYXRpb24gPSBwcm9jZXNzLmFyZ3YucG9wKCkudG9Mb3dlckNhc2UoKTsKICAgIHJlcXVpcmUoJ3Byb2Nlc3MtbWFuYWdlcicpLmVudW1lcmF0ZVByb2Nlc3NlcygpLnRoZW4oZnVuY3Rpb24gKHByb2MpCiAgICB7CiAgICAgICAgZm9yICh2YXIgcCBpbiBwcm9jKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKHByb2NbcF0ucGF0aCAmJiAocHJvY1twXS5wYXRoLnRvTG93ZXJDYXNlKCkgPT0gc2VydmljZUxvY2F0aW9uKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcHJvY2Vzcy5raWxsKHByb2NbcF0ucGlkKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBwcm9jZXNzLmV4aXQoKTsKICAgIH0pOwp9CmNhdGNoIChlKQp7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQ==' +
 | |
|         ' "' + process.execPath + '" & copy "' + cwd + agentfilename + '.update.exe" "' + process.execPath + '" & net start "' + name + '" & erase "' + cwd + agentfilename + '.update.exe"', { wide: true });
 | |
| 
 | |
|     if (name == null)
 | |
|     {
 | |
|         // We can continue with self update for Temp/Console Mode on Windows
 | |
|         var db = null;
 | |
|         var update = cwd + agentfilename + '.update.exe';
 | |
|         var updatedb = cwd + agentfilename + '.update.db';
 | |
|         var parms = windows_getCommandLine(); parms.shift();
 | |
|         
 | |
|         var updatesource = parms.find(function (v) { return (v.startsWith('--updateSourcePath=')); });
 | |
|         if (updatesource == null)
 | |
|         {
 | |
|             parms.push('--updateSourcePath="' + cwd + agentfilename + '"');
 | |
|             updatesource = (cwd + agentfilename).split('.exe'); updatesource.pop(); updatesource = updatesource.join('.exe');
 | |
|             db = updatesource + '.db';
 | |
|             updatesource = (' & move "' + updatedb + '" "' + db + '"') + (' & erase "' + updatedb + '" & move "' + update + '" "' + updatesource + '.exe"');
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             updatesource = updatesource.substring(19).split('.exe');
 | |
|             updatesource.pop(); updatesource = updatesource.join('.exe');
 | |
|             db = updatesource + '.db';
 | |
|             updatesource = (' & move "' + update + '" "' + updatesource + '.exe" & move "' + updatedb + '" "' + db + '" & erase "' + updatedb + '"') + (' & echo move "' + update + '" "' + updatesource + '.exe" & echo move "' + updatedb + '" "' + db + '"');
 | |
|         }
 | |
| 
 | |
|         var tmp = '/C echo copy "' + db + '" "' + updatedb + '" & copy "' + db + '" "' + updatedb + '"' + ' & "' + update + '" ' + parms.join(' ') + updatesource + ' & erase "' + update + '" & echo ERASE "' + update + '"';
 | |
|         arg2 = require('_GenericMarshal').CreateVariable(tmp, { wide: true });
 | |
|     }
 | |
| 
 | |
|     arg1.pointerBuffer().copy(args.toBuffer());
 | |
|     arg2.pointerBuffer().copy(args.toBuffer(), require('_GenericMarshal').PointerSize);
 | |
| 
 | |
|     libc._wexecve(cmd, args, 0);
 | |
| }
 | |
| 
 | |
| // Start a JavaScript based Agent Self-Update
 | |
| function agentUpdate_Start(updateurl, updateoptions) {
 | |
|     // If this value is null
 | |
|     var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
 | |
| 
 | |
|     // If the url starts with *, switch it to use the same protoco, host and port as the control channel.
 | |
|     if (updateurl != null) {
 | |
|         updateurl = getServerTargetUrlEx(updateurl);
 | |
|         if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
 | |
|     }
 | |
| 
 | |
|     if (agentUpdate_Start._selfupdate != null)
 | |
|     {
 | |
|         // We were already called, so we will ignore this duplicate request
 | |
|         if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
 | |
|     }
 | |
|     else {
 | |
|         if (agentUpdate_Start._retryCount == null) { agentUpdate_Start._retryCount = 0; }
 | |
|         if (require('MeshAgent').ARCHID == null && updateurl == null) {
 | |
|             // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
 | |
|             sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
 | |
|             var name = require('MeshAgent').serviceName;
 | |
|             if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; }
 | |
|             if (process.platform == 'win32')
 | |
|             {
 | |
|                 // Special Processing for Temporary/Console Mode Agents on Windows
 | |
|                 var parms = windows_getCommandLine(); // This uses FFI to fetch the command line parameters that the agent was started with
 | |
|                 if (parms.findIndex(function (val) { return (val != null && (val.toUpperCase() == 'RUN' || val.toUpperCase() == 'CONNECT')); }) >= 0)
 | |
|                 {
 | |
|                     // This is a Temporary/Console Mode Agent
 | |
|                     sendConsoleText('This is a temporary/console agent, checking for conflicts with background services...');
 | |
| 
 | |
|                     // Check to see if our binary conflicts with an installed agent
 | |
|                     var agents = _getPotentialServiceNames();
 | |
|                     if (_getPotentialServiceNames().length > 0)
 | |
|                     {
 | |
|                         sendConsoleText('Self update cannot continue because the installed agent (' + agents[0] + ') conflicts with the currently running Temp/Console agent...', sessionid);
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
| 
 | |
|                     sendConsoleText('No conflicts detected...');
 | |
|                     name = null;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // Not running in Temp/Console Mode... No Op here....
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Non Windows Self Update
 | |
|                 try
 | |
|                 {
 | |
|                     var s = require('service-manager').manager.getService(name);
 | |
|                     if (!s.isMe())
 | |
|                     {
 | |
|                         if (process.platform == 'win32') { s.close(); }
 | |
|                         sendConsoleText('Self Update cannot continue, this agent is not an instance of background service (' + name + ')', sessionid);
 | |
|                         return;
 | |
|                     }
 | |
|                     if (process.platform == 'win32') { s.close(); }
 | |
|                 }
 | |
|                 catch (zz)
 | |
|                 {
 | |
|                     sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
 | |
|                     sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             if ((sessionid != null) && (updateurl != null)) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
 | |
|             var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
 | |
|             options.protocol = 'https:';
 | |
|             if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); sendConsoleText('Downloading update from: ' + options.path, sessionid); }
 | |
|             options.rejectUnauthorized = false;
 | |
|             options.checkServerIdentity = function checkServerIdentity(certs) {
 | |
|                 // If the tunnel certificate matches the control channel certificate, accept the connection
 | |
|                 try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
 | |
|                 try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
 | |
| 
 | |
|                 // Check that the certificate is the one expected by the server, fail if not.
 | |
|                 if (checkServerIdentity.servertlshash == null) {
 | |
|                     if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) { return; }
 | |
|                     sendConsoleText('Self Update failed, because the url cannot be verified: ' + updateurl, sessionid);
 | |
|                     sendAgentMessage('Self Update failed, because the url cannot be verified: ' + updateurl, 3);
 | |
|                     throw new Error('BadCert');
 | |
|                 }
 | |
|                 if (certs[0].digest == null) { return; }
 | |
|                 if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) {
 | |
|                     sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
 | |
|                     sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
 | |
|                     throw new Error('BadCert')
 | |
|                 }
 | |
|             }
 | |
|             options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
 | |
|             agentUpdate_Start._selfupdate = require('https').get(options);
 | |
|             agentUpdate_Start._selfupdate.on('error', function (e) {
 | |
|                 sendConsoleText('Self Update failed, because there was a problem trying to download the update from ' + updateurl, sessionid);
 | |
|                 sendAgentMessage('Self Update failed, because there was a problem trying to download the update from ' + updateurl, 3);
 | |
|                 agentUpdate_Start._selfupdate = null;
 | |
|             });
 | |
|             agentUpdate_Start._selfupdate.on('response', function (img)
 | |
|             {
 | |
|                 var self = this;
 | |
|                 this._file = require('fs').createWriteStream(agentfilename + (process.platform=='win32'?'.update.exe':'.update'), { flags: 'wb' });
 | |
|                 this._filehash = require('SHA384Stream').create();
 | |
|                 this._filehash.on('hash', function (h)
 | |
|                 {
 | |
|                     if (updateoptions != null && updateoptions.hash != null)
 | |
|                     {
 | |
|                         if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
 | |
|                         {
 | |
|                             if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); }
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             agentUpdate_Start._retryCount++;
 | |
|                             sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + '), URL: ' + updateurl, sessionid);
 | |
|                             sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + '), URL: ' + updateurl, 3);
 | |
|                             agentUpdate_Start._selfupdate = null;
 | |
| 
 | |
|                             try
 | |
|                             {
 | |
|                                 // We are clearing these two properties, becuase some older agents may not cleanup correctly causing problems with the retry
 | |
|                                 require('https').globalAgent.sockets = {};  
 | |
|                                 require('https').globalAgent.requests = {};
 | |
|                             }
 | |
|                             catch(z)
 | |
|                             {}
 | |
|                             if (needStreamFix)
 | |
|                             {
 | |
|                                 sendConsoleText('This is an older agent that may have an httpstream bug. On next retry will try to fetch the update differently...');
 | |
|                                 needStreamFix = false;
 | |
|                             }
 | |
| 
 | |
|                             if (agentUpdate_Start._retryCount < 4)
 | |
|                             {
 | |
|                                 // Retry the download again
 | |
|                                 sendConsoleText('Self Update will try again in 20 seconds...', sessionid);
 | |
|                                 agentUpdate_Start._timeout = setTimeout(agentUpdate_Start, 20000, updateurl, updateoptions);
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 sendConsoleText('Self Update giving up, too many failures...', sessionid);
 | |
|                                 sendAgentMessage('Self Update giving up, too many failures...', 3);
 | |
|                             }
 | |
|                             return;
 | |
|                         }
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
 | |
|                     }
 | |
| 
 | |
|                     // Send an indication to the server that we got the update download correctly.
 | |
|                     try { require('MeshAgent').SendCommand({ action: 'agentupdatedownloaded' }); } catch (e) { }
 | |
| 
 | |
|                     if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); }
 | |
|                     if (process.platform == 'win32')
 | |
|                     {
 | |
|                         // Use _wexecve() equivalent to perform the update
 | |
|                         windows_execve(name, agentfilename, sessionid);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         var m = require('fs').statSync(process.execPath).mode;
 | |
|                         require('fs').chmodSync(process.cwd() + agentfilename + '.update', m);
 | |
| 
 | |
|                         // remove binary
 | |
|                         require('fs').unlinkSync(process.execPath);
 | |
| 
 | |
|                         // copy update
 | |
|                         require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath);
 | |
|                         require('fs').chmodSync(process.execPath, m);
 | |
| 
 | |
|                         // erase update
 | |
|                         require('fs').unlinkSync(process.cwd() + agentfilename + '.update');
 | |
| 
 | |
|                         switch (process.platform)
 | |
|                         {
 | |
|                             case 'freebsd':
 | |
|                                 bsd_execv(name, agentfilename, sessionid);
 | |
|                                 break;
 | |
|                             case 'linux':
 | |
|                                 linux_execv(name, agentfilename, sessionid);
 | |
|                                 break;
 | |
|                             default:
 | |
|                                 try
 | |
|                                 {
 | |
|                                     // restart service
 | |
|                                     var s = require('service-manager').manager.getService(name);
 | |
|                                     s.restart();
 | |
|                                 }
 | |
|                                 catch (zz)
 | |
|                                 {
 | |
|                                     if (zz.toString() != 'waitExit() aborted because thread is exiting')
 | |
|                                     {
 | |
|                                         sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
 | |
|                                         sendAgentMessage('Self Update encountered an error trying to restart service', 3);
 | |
|                                     }
 | |
|                                 }
 | |
|                                 break;
 | |
|                         }
 | |
|                     }
 | |
|                 });
 | |
| 
 | |
|                 if (!needStreamFix)
 | |
|                 {
 | |
|                     img.pipe(this._file);
 | |
|                     img.pipe(this._filehash);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     img.once('data', function (buffer)
 | |
|                     {
 | |
|                         if(this.immediate)
 | |
|                         {
 | |
|                             clearImmediate(this.immediate);
 | |
|                             this.immediate = null;
 | |
| 
 | |
|                             // No need to apply fix
 | |
|                             self._file.write(buffer);
 | |
|                             self._filehash.write(buffer);
 | |
| 
 | |
|                             this.pipe(self._file);
 | |
|                             this.pipe(self._filehash);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             // Need to apply fix
 | |
|                             this.pipe(self._file);
 | |
|                             this.pipe(self._filehash);
 | |
|                         }
 | |
|                     });
 | |
|                     this.immediate = setImmediate(function (self)
 | |
|                     {
 | |
|                         self.immediate = null;
 | |
|                     },this);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Return p number of spaces 
 | |
| function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
 | |
| 
 | |
| setInterval(function () { sendConsoleText('Timer!'); }, 2000);
 | |
| 
 | |
| var 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); }
 | |
|                     if (i != 0) { while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } }
 | |
|                     x.push(w);
 | |
|                 }
 | |
|             }
 | |
|             if (x.length == 0) return '/';
 | |
|             return x.join('/');
 | |
|         }
 | |
|     };
 | |
| // Convert an object to string with all functions
 | |
| function objToString(x, p, pad, ret) {
 | |
|     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' : ' ');
 | |
|     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) + '}';
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| }
 | |
| 
 | |
| // Get server target url with a custom path
 | |
| function getServerTargetUrl(path) {
 | |
|     var x = require('MeshAgent').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;
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| }
 | |
| 
 | |
| require('MeshAgent').on('Connected', function () {
 | |
|     require('os').name().then(function (v) {
 | |
|         //sendConsoleText("Mesh Agent Recovery Console, OS: " + v);
 | |
|         require('MeshAgent').SendCommand(meshCoreObj);
 | |
|     });
 | |
| });
 | |
| 
 | |
| // Called when receiving control data on websocket
 | |
| function onTunnelControlData(data, ws) {
 | |
|     var obj;
 | |
|     if (ws == null) { ws = this; }
 | |
|     if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
 | |
|     else if (typeof data == 'object') { obj = data; } else { return; }
 | |
|     //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
 | |
|     //console.log('onTunnelControlData: ' + JSON.stringify(data));
 | |
| 
 | |
|     if (obj.action) {
 | |
|         switch (obj.action) {
 | |
|             case 'lock': {
 | |
|                 // Lock the current user out of the desktop
 | |
|                 try {
 | |
|                     if (process.platform == 'win32') {
 | |
|                         MeshServerLog("Locking remote user out of desktop", ws.httprequest);
 | |
|                         var child = require('child_process');
 | |
|                         child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
 | |
|                     }
 | |
|                 } catch (e) { }
 | |
|                 break;
 | |
|             }
 | |
|             default:
 | |
|                 // Unknown action, ignore it.
 | |
|                 break;
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
| 
 | |
|             // Set additional user consent options if present
 | |
|             if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
 | |
| 
 | |
|             break;
 | |
|         }
 | |
|         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': {
 | |
|             // Indicates a change in terminal size
 | |
|             if (process.platform == 'win32') {
 | |
|                 if (ws.httprequest._dispatcher == null) return;
 | |
|                 if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
 | |
|             }
 | |
|             else {
 | |
|                 if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
 | |
|                 if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| require('MeshAgent').AddCommandHandler(function (data)
 | |
| {
 | |
|     if (typeof data == 'object') {
 | |
|         // If this is a console command, parse it and call the console handler
 | |
|         switch (data.action) {
 | |
|             case 'agentupdate':
 | |
|                 agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash, sessionid: data.sessionid });
 | |
|                 break;
 | |
|             case 'msg':
 | |
|                 {
 | |
|                     switch (data.type) {
 | |
|                         case 'console': { // Process a console command
 | |
|                             if ((typeof data.rights != 'number') || ((data.rights & 8) == 0) || ((data.rights & 16) == 0)) break; // Check console rights (Remote Control and Console)
 | |
|                             if (data.value && data.sessionid) {
 | |
|                                 var args = splitArgs(data.value);
 | |
|                                 processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
 | |
|                             }
 | |
|                             break;
 | |
|                         }
 | |
|                         case 'tunnel':
 | |
|                             {
 | |
|                                 if (data.value != null) { // Process a new tunnel connection request
 | |
|                                     // Create a new tunnel object
 | |
|                                     if (data.rights != 4294967295) {
 | |
|                                         MeshServerLog('Tunnel Error: RecoveryCore requires admin rights for tunnels');
 | |
|                                         break;
 | |
|                                     }
 | |
| 
 | |
|                                     var xurl = getServerTargetUrlEx(data.value);
 | |
|                                     if (xurl != null)
 | |
|                                     {
 | |
|                                         xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
 | |
|                                         var woptions = http.parseUri(xurl);
 | |
|                                         woptions.rejectUnauthorized = 0;
 | |
|                                         woptions.perMessageDeflate = false;
 | |
|                                         woptions.checkServerIdentity = function checkServerIdentity(certs) {
 | |
|                                             // If the tunnel certificate matches the control channel certificate, accept the connection
 | |
|                                             try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
 | |
|                                             try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
 | |
| 
 | |
|                                             // Check that the certificate is the one expected by the server, fail if not.
 | |
|                                             if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
 | |
|                                         }
 | |
|                                         woptions.checkServerIdentity.servertlshash = data.servertlshash;
 | |
| 
 | |
| 
 | |
|                                         //sendConsoleText(JSON.stringify(woptions));
 | |
|                                         var tunnel = http.request(woptions);
 | |
|                                         tunnel.on('upgrade', function (response, s, head) {
 | |
|                                             if (require('MeshAgent').idleTimeout != null) {
 | |
|                                                 s.setTimeout(require('MeshAgent').idleTimeout * 1000);
 | |
|                                                 s.on('timeout', function () {
 | |
|                                                     this.ping();
 | |
|                                                     this.setTimeout(require('MeshAgent').idleTimeout * 1000);
 | |
|                                                 });
 | |
|                                             }
 | |
| 
 | |
|                                             this.s = s;
 | |
|                                             s.httprequest = this;
 | |
|                                             s.tunnel = this;
 | |
|                                             s.on('end', function () {
 | |
|                                                 if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
 | |
| 
 | |
|                                                 // If there is a upload or download active on this connection, close the file
 | |
|                                                 if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
 | |
|                                                 if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
 | |
| 
 | |
|                                                 //sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
 | |
|                                                 delete tunnels[this.httprequest.index];
 | |
| 
 | |
|                                                 // Clean up WebSocket
 | |
|                                                 this.removeAllListeners('data');
 | |
|                                             });
 | |
|                                             s.on('data', function (data) {
 | |
|                                                 // If this is upload data, save it to file
 | |
|                                                 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.
 | |
|                                                     }
 | |
|                                                     this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
 | |
|                                                     return;
 | |
|                                                 }
 | |
| 
 | |
|                                                 if (this.httprequest.state == 0) {
 | |
|                                                     // Check if this is a relay connection
 | |
|                                                     if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
 | |
|                                                 }
 | |
|                                                 else {
 | |
|                                                     // Handle tunnel data
 | |
|                                                     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
 | |
|                                                         // Take a look at the protocol
 | |
|                                                         if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
 | |
|                                                         this.httprequest.protocol = parseInt(data);
 | |
|                                                         if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
 | |
|                                                         if (this.httprequest.protocol == 10) {
 | |
|                                                             //
 | |
|                                                             // Basic file transfer
 | |
|                                                             //
 | |
|                                                             var stats = null;
 | |
|                                                             if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
 | |
|                                                             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) {
 | |
|                                                                 //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
 | |
|                                                                 this.write(JSON.stringify({ op: 'ok', size: stats.size }));
 | |
|                                                                 this.httprequest.downloadFile.pipe(this);
 | |
|                                                                 this.httprequest.downloadFile.end = function () { }
 | |
|                                                             } else {
 | |
|                                                                 //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
 | |
|                                                                 this.write(JSON.stringify({ op: 'cancel' }));
 | |
|                                                             }
 | |
|                                                         }
 | |
|                                                         else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) {
 | |
|                                                             //
 | |
|                                                             // Remote Terminal
 | |
|                                                             //
 | |
|                                                             if (process.platform == "win32") {
 | |
|                                                                 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; }
 | |
|                                                                 }
 | |
| 
 | |
|                                                                 // Admin Terminal
 | |
|                                                                 if (require('win-virtual-terminal').supported) {
 | |
|                                                                     // ConPTY PseudoTerminal
 | |
|                                                                     // 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: 'Start', args: [cols, rows] } });
 | |
|                                                                     this.httprequest._dispatcher.ws = this;
 | |
|                                                                     this.httprequest._dispatcher.on('connection', function (c) {
 | |
|                                                                         this.ws._term = c;
 | |
|                                                                         c.pipe(this.ws, { dataTypeSkip: 1 });
 | |
|                                                                         this.ws.pipe(c, { dataTypeSkip: 1 });
 | |
|                                                                     });
 | |
|                                                                 }
 | |
|                                                                 else {
 | |
|                                                                     // Legacy Terminal
 | |
|                                                                     this.httprequest._term = require('win-terminal').Start(80, 25);
 | |
|                                                                     this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
 | |
|                                                                     this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
 | |
|                                                                     this.prependListener('end', function () { this.httprequest._term.end(function () { sendConsoleText('Terminal was closed'); }); });
 | |
|                                                                 }
 | |
|                                                             }
 | |
|                                                             else {
 | |
|                                                                 var env = { HISTCONTROL: 'ignoreboth' };
 | |
|                                                                 if (process.env['LANG']) { env['LANG'] = process.env['LANG']; }
 | |
|                                                                 if (process.env['PATH']) { env['PATH'] = process.env['PATH']; }
 | |
|                                                                 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, env: env };
 | |
| 
 | |
|                                                                 if (require('fs').existsSync('/bin/bash')) {
 | |
|                                                                     this.httprequest.process = childProcess.execFile('/bin/bash', ['bash'], options); // Start bash
 | |
|                                                                 }
 | |
|                                                                 else {
 | |
|                                                                     this.httprequest.process = childProcess.execFile('/bin/sh', ['sh'], options); // Start sh
 | |
|                                                                 }
 | |
| 
 | |
|                                                                 // Spaces at the beginning of lines are needed to hide commands from the command history
 | |
|                                                                 if (process.platform == 'linux') { this.httprequest.process.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
 | |
|                                                                 this.httprequest.process.tunnel = this;
 | |
|                                                                 this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
 | |
|                                                                 this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
 | |
|                                                                 this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
 | |
|                                                                 this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
 | |
|                                                                 this.prependListener('end', function () { this.httprequest.process.kill(); });
 | |
|                                                             }
 | |
|                                                         }
 | |
|                                                     }
 | |
|                                                     else if (this.httprequest.protocol == 5) {
 | |
|                                                         // Process files commands
 | |
|                                                         var cmd = null;
 | |
|                                                         try { cmd = JSON.parse(data); } catch (e) { };
 | |
|                                                         if (cmd == null) { return; }
 | |
|                                                         if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { return; } // If this is control data, handle it now.
 | |
|                                                         if (cmd.action == undefined) { return; }
 | |
|                                                         console.log('action: ', cmd.action);
 | |
| 
 | |
|                                                         //sendConsoleText('CMD: ' + JSON.stringify(cmd));
 | |
| 
 | |
|                                                         if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
 | |
|                                                         //console.log(objToString(cmd, 0, ' '));
 | |
|                                                         switch (cmd.action) {
 | |
|                                                             case 'ls':
 | |
|                                                                 // Send the folder content to the browser
 | |
|                                                                 var response = getDirectoryInfo(cmd.path);
 | |
|                                                                 if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
 | |
|                                                                 this.write(Buffer.from(JSON.stringify(response)));
 | |
|                                                                 break;
 | |
|                                                             case 'mkdir':
 | |
|                                                                 {
 | |
|                                                                     // Create a new empty folder
 | |
|                                                                     fs.mkdirSync(cmd.path);
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             case 'rm':
 | |
|                                                                 {
 | |
|                                                                     // Delete, possibly recursive delete
 | |
|                                                                     for (var i in cmd.delfiles) {
 | |
|                                                                         try { deleteFolderRecursive(path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { }
 | |
|                                                                     }
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             case 'rename':
 | |
|                                                                 {
 | |
|                                                                     // Rename a file or folder
 | |
|                                                                     var oldfullpath = path.join(cmd.path, cmd.oldname);
 | |
|                                                                     var newfullpath = path.join(cmd.path, cmd.newname);
 | |
|                                                                     try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             case 'findfile':
 | |
|                                                                 {
 | |
|                                                                     // Search for files
 | |
|                                                                     var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
 | |
|                                                                     if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
 | |
|                                                                     this._search = r;
 | |
|                                                                     r.socket = this;
 | |
|                                                                     r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
 | |
|                                                                     r.socket.path = cmd.path;   // Search path
 | |
|                                                                     r.on('result', function (str) { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: str.substring(this.socket.path.length), reqid: this.socket.reqid }))); } catch (ex) { } });
 | |
|                                                                     r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             case 'cancelfindfile':
 | |
|                                                                 {
 | |
|                                                                     if (this._search) { this._search.cancel(); this._search = null; }
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             case 'download':
 | |
|                                                                 {
 | |
|                                                                     // Download a file
 | |
|                                                                     var sendNextBlock = 0;
 | |
|                                                                     if (cmd.sub == 'start') { // Setup the download
 | |
|                                                                         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'; }
 | |
|                                                                             }
 | |
|                                                                         }
 | |
|                                                                         MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
 | |
|                                                                         if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
 | |
|                                                                         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
 | |
|                                                                         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; }
 | |
|                                                                     }
 | |
|                                                                     // Send the next download block(s)
 | |
|                                                                     while (sendNextBlock > 0) {
 | |
|                                                                         sendNextBlock--;
 | |
|                                                                         var buf = Buffer.alloc(16384);
 | |
|                                                                         var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
 | |
|                                                                         this.filedownload.ptr += len;
 | |
|                                                                         if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
 | |
|                                                                         this.write(buf.slice(0, len + 4)); // Write as binary
 | |
|                                                                     }
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             case 'upload':
 | |
|                                                                 {
 | |
|                                                                     // Upload a file, browser to agent
 | |
|                                                                     if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
 | |
|                                                                     if (cmd.path == undefined) break;
 | |
|                                                                     var filepath = cmd.name ? pathjoin(cmd.path, cmd.name) : cmd.path;
 | |
|                                                                     this.httprequest.uploadFilePath = filepath;
 | |
|                                                                     MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
 | |
|                                                                     try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
 | |
|                                                                     this.httprequest.uploadFileid = cmd.reqid;
 | |
|                                                                     if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
 | |
|                                                                     break;
 | |
|                                                                 }
 | |
|                                                             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;
 | |
|                                                                 }
 | |
|                                                             case 'copy': {
 | |
|                                                                 // Copy a bunch of files from scpath to dspath
 | |
|                                                                 for (var i in cmd.names) {
 | |
|                                                                     var sc = path.join(cmd.scpath, cmd.names[i]), ds = path.join(cmd.dspath, cmd.names[i]);
 | |
|                                                                     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 = path.join(cmd.scpath, cmd.names[i]), ds = path.join(cmd.dspath, cmd.names[i]);
 | |
|                                                                     if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
 | |
|                                                                 }
 | |
|                                                                 break;
 | |
|                                                             }
 | |
|                                                         }
 | |
|                                                     }
 | |
|                                                 }
 | |
|                                             });
 | |
|                                         });
 | |
|                                         tunnel.onerror = function (e) { sendConsoleText("ERROR: " + JSON.stringify(e)); }
 | |
|                                         tunnel.sessionid = data.sessionid;
 | |
|                                         tunnel.rights = data.rights;
 | |
|                                         tunnel.state = 0;
 | |
|                                         tunnel.url = xurl;
 | |
|                                         tunnel.protocol = 0;
 | |
|                                         tunnel.tcpaddr = data.tcpaddr;
 | |
|                                         tunnel.tcpport = data.tcpport;
 | |
|                                         tunnel.end();
 | |
|                                         // Put the tunnel in the tunnels list
 | |
|                                         var index = nextTunnelIndex++;
 | |
|                                         tunnel.index = index;
 | |
|                                         tunnels[index] = tunnel;
 | |
| 
 | |
|                                         //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
 | |
|                                     }
 | |
|                                 }
 | |
|                                 break;
 | |
|                             }
 | |
| 
 | |
|                         default:
 | |
|                             // Unknown action, ignore it.
 | |
|                             break;
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
|             default:
 | |
|                 // Unknown action, ignore it.
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
| });
 | |
| 
 | |
| function processConsoleCommand(cmd, args, rights, sessionid) {
 | |
|     try {
 | |
|         var response = null;
 | |
|         switch (cmd)
 | |
|         {
 | |
|             default:
 | |
|                 { // This is an unknown command, return an error message
 | |
|                     response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.';
 | |
|                     break;
 | |
|                 }
 | |
|             case 'commandline':
 | |
|                 {
 | |
|                     if (process.platform == 'win32')
 | |
|                     {
 | |
|                         response = JSON.stringify(windows_getCommandLine(), null, 1);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.';
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             case 'help':
 | |
|                 response = "Available commands are: agentupdate, agentupdateex, dbkeys, dbget, dbset, dbcompact, eval, netinfo, osinfo, setdebug, versions.";
 | |
|                 break;
 | |
|             case '_descriptors':
 | |
|                 response = 'Open Descriptors: ' + JSON.stringify(getOpenDescriptors());
 | |
|                 break;
 | |
|             case 'versions':
 | |
|                 response = JSON.stringify(process.versions, null, '  ');
 | |
|                 break;
 | |
|             case 'agentupdate':
 | |
|                 // Request that the server send a agent update command
 | |
|                 require('MeshAgent').SendCommand({ action: 'agentupdate', sessionid: sessionid });
 | |
|                 break;
 | |
|             case 'agentupdateex':
 | |
|                 // Perform an direct agent update without requesting any information from the server, this should not typically be used.
 | |
|                 if (args['_'].length == 1) {
 | |
|                     if (args['_'][0].startsWith('https://')) { agentUpdate_Start(args['_'][0], { sessionid: sessionid }); } else { response = "Usage: agentupdateex https://server/path"; }
 | |
|                 } else {
 | |
|                     agentUpdate_Start(null, { sessionid: sessionid });
 | |
|                 }
 | |
|                 break;
 | |
|             case 'eval':
 | |
|                 { // Eval JavaScript
 | |
|                     if (args['_'].length < 1) {
 | |
|                         response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
 | |
|                     } else {
 | |
|                         response = JSON.stringify(require('MeshAgent').eval(args['_'][0])); // This can only be run by trusted administrator.
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
|             case 'setdebug':
 | |
|                 {
 | |
|                     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); } }
 | |
|                     break;
 | |
|                 }
 | |
|             case 'osinfo': { // Return the operating system information
 | |
|                 var i = 1;
 | |
|                 if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
 | |
|                 for (var j = 0; j < i; j++) {
 | |
|                     var pr = require('os').name();
 | |
|                     pr.sessionid = sessionid;
 | |
|                     pr.then(function (v) {
 | |
|                         sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
 | |
|                     });
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             case 'dbkeys': { // Return all data store keys
 | |
|                 response = JSON.stringify(db.Keys);
 | |
|                 break;
 | |
|             }
 | |
|             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 '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 'netinfo': { // Show network interface information
 | |
|                 //response = objToString(mesh.NetInfo, 0, ' ');
 | |
|                 var interfaces = require('os').networkInterfaces();
 | |
|                 response = objToString(interfaces, 0, ' ', true);
 | |
|                 break;
 | |
|             }
 | |
|             case 'name':
 | |
|                 {
 | |
|                     response = 'Service Name = ' + require('MeshAgent').serviceName;
 | |
|                 }
 | |
|                 break;
 | |
|         }
 | |
|     } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
 | |
|     if (response != null) { sendConsoleText(response, sessionid); }
 | |
| }
 | |
| 
 | |
| // 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 = '/'; }
 | |
|         var results = null, xpath = path.join(reqpath, '*');
 | |
|         //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
 | |
|         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 = path.join(reqpath, results[i]);
 | |
|                     //if (process.platform == "win32") { p = p.split('/').join('\\'); }
 | |
|                     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;
 | |
| }
 | |
| // Delete a directory with a files and directories within it
 | |
| function deleteFolderRecursive(path, rec) {
 | |
|     if (fs.existsSync(path)) {
 | |
|         if (rec == true) {
 | |
|             fs.readdirSync(path.join(path, '*')).forEach(function (file, index) {
 | |
|                 var curPath = path.join(path, file);
 | |
|                 if (fs.statSync(curPath).isDirectory()) { // recurse
 | |
|                     deleteFolderRecursive(curPath, true);
 | |
|                 } else { // delete file
 | |
|                     fs.unlinkSync(curPath);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         fs.unlinkSync(path);
 | |
|     }
 | |
| };
 |