Added workaround for platforms where /var/run/utmp is touched too frequently Fixed Global/Function scope problems

Added workaround for platforms where /var/run/utmp is touched too frequently Fixed Global/Function scope problems

Bryan Roe committed Jan 24, 2021 at 15:19 UTC 1a65fd5ab68139773c851dfe0eaaf24963af4d82
1 file changed +4216 -3661
agents/meshcore.js
+4216 -3661
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 -process.on('uncaughtException', function (ex) {
17 +process.on('uncaughtException', function (ex)
18 +{
19 require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
20 });
21
@@ -41,9 +42,11 @@ var MESHRIGHT_CHATNOTIFY = 16384;
42 var MESHRIGHT_UNINSTALL = 32768;
43 var MESHRIGHT_NODESKTOP = 65536;
44
44 -if (require('MeshAgent').ARCHID == null) {
45 +if (require('MeshAgent').ARCHID == null)
46 +{
47 var id = null;
46 - switch (process.platform) {
48 + switch (process.platform)
49 + {
50 case 'win32':
51 id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4;
52 break;
@@ -51,10 +54,12 @@ if (require('MeshAgent').ARCHID == null) {
54 id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30;
55 break;
56 case 'darwin':
54 - try {
57 + try
58 + {
59 id = require('os').arch() == 'x64' ? 16 : 29;
60 }
57 - catch (xx) {
61 + catch (xx)
62 + {
63 id = 16;
64 }
65 break; break;
@@ -62,609 +67,685 @@ if (require('MeshAgent').ARCHID == null) {
67 if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); }
68 }
69
65 -function createMeshCore(agent) {
66 - var obj = {};
67 - var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server.
68 - var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server.
69 - var debugConsole = (_MSH().debugConsole == 1);
70 -
71 - if (process.platform == 'win32' && require('user-sessions').isRoot()) {
72 - // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
73 - try {
74 - var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
75 - try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { }
76 - if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
77 - } catch (x) { }
78 -
79 - // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
80 - var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
81 - try {
82 - svcname = require('MeshAgent').serviceName;
83 - }
84 - catch (x) {
85 - }
70 +var obj = {};
71 +var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server.
72 +var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server.
73 +var debugConsole = (_MSH().debugConsole == 1);
74
87 - try {
88 - var meshCheck = false;
89 - try { meshCheck = require('service-manager').manager.getService(svcname).isMe(); } catch (e) { }
90 - if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService(svcname)) { require('win-bcd').enableSafeModeService(svcname); }
91 - } catch (e) { }
75 +if (process.platform == 'win32' && require('user-sessions').isRoot())
76 +{
77 + // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
78 + try
79 + {
80 + var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
81 + try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { }
82 + if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
83 + } catch (x) { }
84 +
85 + // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
86 + var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
87 + try
88 + {
89 + svcname = require('MeshAgent').serviceName;
90 }
91 + catch (x)
92 + {
93 + }
94 +
95 + try
96 + {
97 + var meshCheck = false;
98 + try { meshCheck = require('service-manager').manager.getService(svcname).isMe(); } catch (e) { }
99 + if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService(svcname)) { require('win-bcd').enableSafeModeService(svcname); }
100 + } catch (e) { }
101 +}
102
94 - if (process.platform == 'darwin' && !process.versions) {
95 - // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
96 - var child = require('child_process').execFile('/bin/sh', ['sh']);
103 +if (process.platform == 'darwin' && !process.versions)
104 +{
105 + // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
106 + var child = require('child_process').execFile('/bin/sh', ['sh']);
107 + child.stdout.str = '';
108 + child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
109 + 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, \">\"); ");
110 + 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]; } }");
111 + child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
112 + child.waitExit();
113 + if (child.stdout.str.trim() == 'Crashed')
114 + {
115 + child = require('child_process').execFile('/bin/sh', ['sh']);
116 child.stdout.str = '';
117 child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
99 - 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, \">\"); ");
100 - 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]; } }");
101 - child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
118 + child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
119 child.waitExit();
103 - if (child.stdout.str.trim() == 'Crashed') {
104 - child = require('child_process').execFile('/bin/sh', ['sh']);
105 - child.stdout.str = '';
106 - child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
107 - child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
108 - child.waitExit();
109 -
110 - if (parseInt(child.stdout.str.trim()) == process.pid) {
111 - // The currently running MeshAgent is us, so we can continue with the update
112 - var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
113 - var tokens = plist.split('<key>KeepAlive</key>');
114 - if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
115 - var tmp = tokens[1].split('</dict>');
116 - tmp.shift();
117 - tokens[1] = '\n <true/>' + tmp.join('</dict>');
118 - tokens = tokens.join('<key>KeepAlive</key>');
119 -
120 - require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
121 -
122 - var fix = '';
123 - fix += ("function macosRepair()\n");
124 - fix += ("{\n");
125 - fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
126 - fix += (" child.stdout.str = '';\n");
127 - fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
128 - fix += (" child.stderr.on('data', function (chunk) { });\n");
129 - fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
130 - fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
131 - fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
132 - fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
133 - fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
134 - fix += (" child.waitExit();\n");
135 - fix += ("}\n");
136 - fix += ("macosRepair();\n");
137 - fix += ("process.exit();\n");
138 - require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
139 -
140 - var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
141 - plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
142 - plist += '<plist version="1.0">\n';
143 - plist += ' <dict>\n';
144 - plist += ' <key>Label</key>\n';
145 - plist += (' <string>meshagentRepair</string>\n');
146 - plist += ' <key>ProgramArguments</key>\n';
147 - plist += ' <array>\n';
148 - plist += (' <string>' + process.execPath + '</string>\n');
149 - plist += ' <string>macosRepair.js</string>\n';
150 - plist += ' </array>\n';
151 - plist += ' <key>WorkingDirectory</key>\n';
152 - plist += (' <string>' + process.cwd() + '</string>\n');
153 - plist += ' <key>RunAtLoad</key>\n';
154 - plist += ' <true/>\n';
155 - plist += ' </dict>\n';
156 - plist += '</plist>';
157 - require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
158 -
159 - child = require('child_process').execFile('/bin/sh', ['sh']);
160 - child.stdout.str = '';
161 - child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
162 - child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
163 - child.waitExit();
164 - }
120 +
121 + if (parseInt(child.stdout.str.trim()) == process.pid)
122 + {
123 + // The currently running MeshAgent is us, so we can continue with the update
124 + var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
125 + var tokens = plist.split('<key>KeepAlive</key>');
126 + if (tokens[1].split('>')[0].split('<')[1] == 'dict')
127 + {
128 + var tmp = tokens[1].split('</dict>');
129 + tmp.shift();
130 + tokens[1] = '\n <true/>' + tmp.join('</dict>');
131 + tokens = tokens.join('<key>KeepAlive</key>');
132 +
133 + require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
134 +
135 + var fix = '';
136 + fix += ("function macosRepair()\n");
137 + fix += ("{\n");
138 + fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
139 + fix += (" child.stdout.str = '';\n");
140 + fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
141 + fix += (" child.stderr.on('data', function (chunk) { });\n");
142 + fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
143 + fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
144 + fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
145 + fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
146 + fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
147 + fix += (" child.waitExit();\n");
148 + fix += ("}\n");
149 + fix += ("macosRepair();\n");
150 + fix += ("process.exit();\n");
151 + require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
152 +
153 + var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
154 + plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
155 + plist += '<plist version="1.0">\n';
156 + plist += ' <dict>\n';
157 + plist += ' <key>Label</key>\n';
158 + plist += (' <string>meshagentRepair</string>\n');
159 + plist += ' <key>ProgramArguments</key>\n';
160 + plist += ' <array>\n';
161 + plist += (' <string>' + process.execPath + '</string>\n');
162 + plist += ' <string>macosRepair.js</string>\n';
163 + plist += ' </array>\n';
164 + plist += ' <key>WorkingDirectory</key>\n';
165 + plist += (' <string>' + process.cwd() + '</string>\n');
166 + plist += ' <key>RunAtLoad</key>\n';
167 + plist += ' <true/>\n';
168 + plist += ' </dict>\n';
169 + plist += '</plist>';
170 + require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
171 +
172 + child = require('child_process').execFile('/bin/sh', ['sh']);
173 + child.stdout.str = '';
174 + child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
175 + child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
176 + child.waitExit();
177 }
178 }
179 }
180 +}
181
169 - // Add an Intel AMT event to the log
170 - function addAmtEvent(msg) {
171 - if (obj.amtevents == null) { obj.amtevents = []; }
172 - var d = new Date();
173 - obj.amtevents.push(zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg);
174 - if (obj.amtevents.length > 100) { obj.amtevents.splice(0, obj.amtevents.length - 100); }
175 - }
176 - function zeroPad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); }
177 -
178 -
179 - // Create Secure IPC for Diagnostic Agent Communications
180 - obj.DAIPC = require('net').createServer();
181 - if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (e) { } }
182 - obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
183 - try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
184 - obj.DAIPC._daipc = [];
185 - obj.DAIPC.on('connection', function (c) {
186 - c._send = function (j) {
187 - var data = JSON.stringify(j);
188 - var packet = Buffer.alloc(data.length + 4);
189 - packet.writeUInt32LE(data.length + 4, 0);
190 - Buffer.from(data).copy(packet, 4);
191 - this.write(packet);
192 - };
193 - this._daipc.push(c);
194 - c.parent = this;
195 - c.on('end', function () { removeRegisteredApp(this); });
196 - c.on('data', function (chunk) {
197 - if (chunk.length < 4) { this.unshift(chunk); return; }
198 - var len = chunk.readUInt32LE(0);
199 - if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
200 - if (chunk.length < len) { this.unshift(chunk); return; }
201 -
202 - var data = chunk.slice(4, len);
203 - try { data = JSON.parse(data.toString()); } catch (e) { }
204 - if ((data == null) || (typeof data.cmd != 'string')) return;
205 -
206 - try {
207 - switch (data.cmd) {
208 - case 'requesthelp':
209 - if (this._registered == null) return;
210 - sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
211 - var help = {};
212 - help[this._registered] = data.value;
213 - try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
214 - MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
215 - break;
216 - case 'cancelhelp':
217 - if (this._registered == null) return;
218 - sendConsoleText('Cancel Help (' + this._registered + ')');
219 - try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
220 - break;
221 - case 'register':
222 - if (typeof data.value == 'string') {
223 - this._registered = data.value;
224 - var apps = {};
225 - apps[data.value] = 1;
226 - try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
227 - this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
228 - }
229 - break;
230 - case 'query':
231 - switch (data.value) {
232 - case 'connection':
233 - data.result = require('MeshAgent').ConnectedServer;
234 - this._send(data);
235 - break;
236 - case 'descriptors':
237 - require('ChainViewer').getSnapshot().then(function (f) {
238 - this.tag.payload.result = f;
239 - this.tag.ipc._send(this.tag.payload);
240 - }).parentPromise.tag = { ipc: this, payload: data };
241 - break;
242 - case 'timerinfo':
243 - data.result = require('ChainViewer').getTimerInfo();
244 - this._send(data);
245 - break;
246 - }
247 - break;
248 - case 'amtstate':
249 - if (amt == null) return;
250 - var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
251 - func.pipe = this;
252 - amt.getMeiState(11, func);
253 - break;
254 - case 'sessions':
255 - this._send({ cmd: 'sessions', sessions: tunnelUserCount });
256 - break;
257 - case 'meshToolInfo':
258 - try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (e) { }
259 - break;
260 - case 'console':
261 - if (debugConsole) {
262 - var args = splitArgs(data.value);
263 - processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe');
264 - }
265 - break;
266 - }
182 +// Add an Intel AMT event to the log
183 +function addAmtEvent(msg)
184 +{
185 + if (obj.amtevents == null) { obj.amtevents = []; }
186 + var d = new Date();
187 + obj.amtevents.push(zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg);
188 + if (obj.amtevents.length > 100) { obj.amtevents.splice(0, obj.amtevents.length - 100); }
189 +}
190 +function zeroPad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); }
191 +
192 +
193 +// Create Secure IPC for Diagnostic Agent Communications
194 +obj.DAIPC = require('net').createServer();
195 +if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (e) { } }
196 +obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
197 +try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
198 +obj.DAIPC._daipc = [];
199 +obj.DAIPC.on('connection', function (c)
200 +{
201 + c._send = function (j)
202 + {
203 + var data = JSON.stringify(j);
204 + var packet = Buffer.alloc(data.length + 4);
205 + packet.writeUInt32LE(data.length + 4, 0);
206 + Buffer.from(data).copy(packet, 4);
207 + this.write(packet);
208 + };
209 + this._daipc.push(c);
210 + c.parent = this;
211 + c.on('end', function () { removeRegisteredApp(this); });
212 + c.on('data', function (chunk)
213 + {
214 + if (chunk.length < 4) { this.unshift(chunk); return; }
215 + var len = chunk.readUInt32LE(0);
216 + if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
217 + if (chunk.length < len) { this.unshift(chunk); return; }
218 +
219 + var data = chunk.slice(4, len);
220 + try { data = JSON.parse(data.toString()); } catch (e) { }
221 + if ((data == null) || (typeof data.cmd != 'string')) return;
222 +
223 + try
224 + {
225 + switch (data.cmd)
226 + {
227 + case 'requesthelp':
228 + if (this._registered == null) return;
229 + sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
230 + var help = {};
231 + help[this._registered] = data.value;
232 + try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
233 + MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
234 + break;
235 + case 'cancelhelp':
236 + if (this._registered == null) return;
237 + sendConsoleText('Cancel Help (' + this._registered + ')');
238 + try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
239 + break;
240 + case 'register':
241 + if (typeof data.value == 'string')
242 + {
243 + this._registered = data.value;
244 + var apps = {};
245 + apps[data.value] = 1;
246 + try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
247 + this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
248 + }
249 + break;
250 + case 'query':
251 + switch (data.value)
252 + {
253 + case 'connection':
254 + data.result = require('MeshAgent').ConnectedServer;
255 + this._send(data);
256 + break;
257 + case 'descriptors':
258 + require('ChainViewer').getSnapshot().then(function (f)
259 + {
260 + this.tag.payload.result = f;
261 + this.tag.ipc._send(this.tag.payload);
262 + }).parentPromise.tag = { ipc: this, payload: data };
263 + break;
264 + case 'timerinfo':
265 + data.result = require('ChainViewer').getTimerInfo();
266 + this._send(data);
267 + break;
268 + }
269 + break;
270 + case 'amtstate':
271 + if (amt == null) return;
272 + var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
273 + func.pipe = this;
274 + amt.getMeiState(11, func);
275 + break;
276 + case 'sessions':
277 + this._send({ cmd: 'sessions', sessions: tunnelUserCount });
278 + break;
279 + case 'meshToolInfo':
280 + try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (e) { }
281 + break;
282 + case 'console':
283 + if (debugConsole)
284 + {
285 + var args = splitArgs(data.value);
286 + processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe');
287 + }
288 + break;
289 }
268 - catch (e) { removeRegisteredApp(this); this.end(); return; }
269 - });
290 + }
291 + catch (e) { removeRegisteredApp(this); this.end(); return; }
292 });
293 +});
294
272 - // Send current sessions to registered apps
273 - function broadcastSessionsToRegisteredApps(x) {
274 - broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
275 - }
295 +// Send current sessions to registered apps
296 +function broadcastSessionsToRegisteredApps(x)
297 +{
298 + broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
299 +}
300
277 - // Send this object to all registered local applications
278 - function broadcastToRegisteredApps(x) {
279 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
280 - for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
281 - }
301 +// Send this object to all registered local applications
302 +function broadcastToRegisteredApps(x)
303 +{
304 + if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
305 + for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
306 +}
307
283 - // Send this object to a specific registered local applications
284 - function sendToRegisteredApp(appid, x) {
285 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
286 - for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } }
287 - }
308 +// Send this object to a specific registered local applications
309 +function sendToRegisteredApp(appid, x)
310 +{
311 + if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
312 + for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } }
313 +}
314
289 - // Send list of registered apps to the server
290 - function updateRegisteredAppsToServer() {
291 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
292 - var apps = {};
293 - for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
294 - try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
295 - }
315 +// Send list of registered apps to the server
316 +function updateRegisteredAppsToServer()
317 +{
318 + if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
319 + var apps = {};
320 + for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
321 + try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
322 +}
323 +
324 +// Remove a registered app
325 +function removeRegisteredApp(pipe)
326 +{
327 + for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
328 + if (pipe._registered != null) updateRegisteredAppsToServer();
329 +}
330
297 - // Remove a registered app
298 - function removeRegisteredApp(pipe) {
299 - for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
300 - if (pipe._registered != null) updateRegisteredAppsToServer();
331 +function diagnosticAgent_uninstall()
332 +{
333 + require('service-manager').manager.uninstallService('meshagentDiagnostic');
334 + require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
335 +}
336 +function diagnosticAgent_installCheck(install)
337 +{
338 + try
339 + {
340 + var diag = require('service-manager').manager.getService('meshagentDiagnostic');
341 + return (diag);
342 + }
343 + catch (e)
344 + {
345 }
346 + if (!install) { return (null); }
347
303 - function diagnosticAgent_uninstall() {
304 - require('service-manager').manager.uninstallService('meshagentDiagnostic');
305 - require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
306 - };
307 - function diagnosticAgent_installCheck(install) {
308 - try {
309 - var diag = require('service-manager').manager.getService('meshagentDiagnostic');
310 - return (diag);
311 - }
312 - catch (e) {
313 - }
314 - if (!install) { return (null); }
315 -
316 - var svc = null;
317 - try {
318 - require('service-manager').manager.installService(
319 - {
320 - name: 'meshagentDiagnostic',
321 - displayName: "Mesh Agent Diagnostic Service",
322 - description: "Mesh Agent Diagnostic Service",
323 - servicePath: process.execPath,
324 - parameters: ['-recovery']
325 - //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
326 - });
327 - svc = require('service-manager').manager.getService('meshagentDiagnostic');
328 - }
329 - catch (e) {
330 - return (null);
331 - }
332 - var proxyConfig = require('global-tunnel').proxyConfig;
333 - var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
334 - var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
335 - ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
336 - ddb.Put('disableUpdate', '1');
337 - ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
338 - ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
339 - ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
340 - if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
341 - if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
342 - if (proxyConfig) {
343 - ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
344 - } else {
345 - ddb.Put('ignoreProxyFile', '1');
346 - }
348 + var svc = null;
349 + try
350 + {
351 + require('service-manager').manager.installService(
352 + {
353 + name: 'meshagentDiagnostic',
354 + displayName: "Mesh Agent Diagnostic Service",
355 + description: "Mesh Agent Diagnostic Service",
356 + servicePath: process.execPath,
357 + parameters: ['-recovery']
358 + //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
359 + });
360 + svc = require('service-manager').manager.getService('meshagentDiagnostic');
361 + }
362 + catch (e)
363 + {
364 + return (null);
365 + }
366 + var proxyConfig = require('global-tunnel').proxyConfig;
367 + var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
368 + var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
369 + ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
370 + ddb.Put('disableUpdate', '1');
371 + ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
372 + ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
373 + ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
374 + if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
375 + if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
376 + if (proxyConfig)
377 + {
378 + ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
379 + }
380 + else
381 + {
382 + ddb.Put('ignoreProxyFile', '1');
383 + }
384
348 - require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
349 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
385 + require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
386 + require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
387
351 - delete ddb;
388 + delete ddb;
389
353 - // Set a recurrent task, to run the Diagnostic Agent every 2 days
354 - 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' });
355 - //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
390 + // Set a recurrent task, to run the Diagnostic Agent every 2 days
391 + 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' });
392 + //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
393
357 - return (svc);
358 - }
394 + return (svc);
395 +}
396
360 - // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
361 - if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
362 - // Setup manual battery monitoring
363 - require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
364 - if (require('MeshAgent')._batteryFileTimer != null) return;
365 - require('MeshAgent')._batteryFileTimer = setTimeout(function () {
366 - try {
367 - require('MeshAgent')._batteryFileTimer = null;
368 - var data = null;
369 - try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
370 - if ((data != null) && (data.length < 10)) {
371 - data = data.split(',');
372 - if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
373 - var level = parseInt(data[1]);
374 - if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
375 - }
397 +// Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
398 +if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null))
399 +{
400 + // Setup manual battery monitoring
401 + require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function ()
402 + {
403 + if (require('MeshAgent')._batteryFileTimer != null) return;
404 + require('MeshAgent')._batteryFileTimer = setTimeout(function ()
405 + {
406 + try
407 + {
408 + require('MeshAgent')._batteryFileTimer = null;
409 + var data = null;
410 + try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
411 + if ((data != null) && (data.length < 10))
412 + {
413 + data = data.split(',');
414 + if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc')))
415 + {
416 + var level = parseInt(data[1]);
417 + if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
418 }
377 - } catch (e) { }
378 - }, 1000);
379 - });
380 - } else {
381 - // Setup normal battery monitoring
382 - if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) {
383 - require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
384 - _battLevelChanged.self._currentBatteryLevel = val;
385 - _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
386 - };
387 - require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
388 - require('MeshAgent')._powerChanged = function _powerChanged(val) {
389 - _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
390 - _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
391 - };
392 - require('MeshAgent')._powerChanged.self = require('MeshAgent');
393 - require('MeshAgent').on('Connected', function (status) {
394 - if (status == 0) {
395 - require('power-monitor').removeListener('acdc', this._powerChanged);
396 - require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
397 - } else {
398 - require('power-monitor').on('acdc', this._powerChanged);
399 - require('power-monitor').on('batteryLevel', this._battLevelChanged);
419 }
401 - });
402 - }
420 + } catch (e) { }
421 + }, 1000);
422 + });
423 +}
424 +else
425 +{
426 + // Setup normal battery monitoring
427 + if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered())
428 + {
429 + require('MeshAgent')._battLevelChanged = function _battLevelChanged(val)
430 + {
431 + _battLevelChanged.self._currentBatteryLevel = val;
432 + _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
433 + };
434 + require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
435 + require('MeshAgent')._powerChanged = function _powerChanged(val)
436 + {
437 + _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
438 + _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
439 + };
440 + require('MeshAgent')._powerChanged.self = require('MeshAgent');
441 + require('MeshAgent').on('Connected', function (status)
442 + {
443 + if (status == 0)
444 + {
445 + require('power-monitor').removeListener('acdc', this._powerChanged);
446 + require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
447 + } else
448 + {
449 + require('power-monitor').on('acdc', this._powerChanged);
450 + require('power-monitor').on('batteryLevel', this._battLevelChanged);
451 + }
452 + });
453 }
454 +}
455
456
406 - /*
407 - function borderController() {
408 - this.container = null;
409 - this.Start = function Start(user) {
410 - if (this.container == null) {
411 - if (process.platform == 'win32') {
412 - try {
413 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
414 - } catch (e) {
415 - this.container = require('ScriptContainer').Create({ processIsolation: 1 });
416 - }
417 - } else {
418 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
457 +// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
458 +var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
459 +
460 +// Get the operating system description string
461 +try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (e) { }
462 +
463 +
464 +// Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
465 +try
466 +{
467 + var userSession = require('user-sessions');
468 + userSession.on('changed', function onUserSessionChanged()
469 + {
470 + userSession.enumerateUsers().then(function (users)
471 + {
472 + if (process.platform == 'linux')
473 + {
474 + if (userSession._startTime == null)
475 + {
476 + userSession._startTime = Date.now();
477 + userSession._count = users.length;
478 + }
479 + else if (Date.now() - userSession._startTime < 10000 && users.length == userSession._count)
480 + {
481 + userSession.removeAllListeners('changed');
482 + return;
483 }
420 - this.container.parent = this;
421 - this.container.addModule('monitor-info', getJSModule('monitor-info'));
422 - this.container.addModule('monitor-border', getJSModule('monitor-border'));
423 - this.container.addModule('promise', getJSModule('promise'));
424 - this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
425 - this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
426 - }
427 - }
428 - this.Stop = function Stop() {
429 - if (this.container != null) {
430 - this._container = this.container;
431 - this._container.parent = this;
432 - this.container = null;
433 - this._container.exit();
484 }
435 - }
436 - }
437 - obj.borderManager = new borderController();
438 - */
485
440 - // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
441 - var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
442 -
443 - // Get the operating system description string
444 - try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (e) { }
445 -
446 - // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
447 - try {
448 - var userSession = require('user-sessions');
449 - userSession.on('changed', function onUserSessionChanged() {
450 - userSession.enumerateUsers().then(function (users) {
451 - var u = [], a = users.Active;
452 - for (var i = 0; i < a.length; i++) {
453 - var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
454 - if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
455 - }
456 - meshCoreObj.users = u;
457 - meshCoreObjChanged();
458 - });
486 + var u = [], a = users.Active;
487 + for (var i = 0; i < a.length; i++)
488 + {
489 + var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
490 + if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
491 + }
492 + meshCoreObj.users = u;
493 + meshCoreObjChanged();
494 });
460 - userSession.emit('changed');
461 - //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
462 - //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
463 - } catch (e) { }
495 + });
496 + userSession.emit('changed');
497 + //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
498 + //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
499 +} catch (e) { }
500 +
501 +var meshServerConnectionState = 0;
502 +var tunnels = {};
503 +var lastNetworkInfo = null;
504 +var lastPublicLocationInfo = null;
505 +var selfInfoUpdateTimer = null;
506 +var http = require('http');
507 +var net = require('net');
508 +var fs = require('fs');
509 +var rtc = require('ILibWebRTC');
510 +var amt = null;
511 +var processManager = require('process-manager');
512 +var wifiScannerLib = null;
513 +var wifiScanner = null;
514 +var networkMonitor = null;
515 +var nextTunnelIndex = 1;
516 +var apftunnel = null;
517 +var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
518 +
519 +// Add to the server event log
520 +function MeshServerLog(msg, state)
521 +{
522 + if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
523 + if (state)
524 + {
525 + if (state.userid) { msg.userid = state.userid; }
526 + if (state.username) { msg.username = state.username; }
527 + if (state.sessionid) { msg.sessionid = state.sessionid; }
528 + if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
529 + }
530 + mesh.SendCommand(msg);
531 +}
532
465 - var meshServerConnectionState = 0;
466 - var tunnels = {};
467 - var lastNetworkInfo = null;
468 - var lastPublicLocationInfo = null;
469 - var selfInfoUpdateTimer = null;
470 - var http = require('http');
471 - var net = require('net');
472 - var fs = require('fs');
473 - var rtc = require('ILibWebRTC');
474 - var amt = null;
475 - var processManager = require('process-manager');
476 - var wifiScannerLib = null;
477 - var wifiScanner = null;
478 - var networkMonitor = null;
479 - var nextTunnelIndex = 1;
480 - var apftunnel = null;
481 - var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
482 -
483 - // Add to the server event log
484 - function MeshServerLog(msg, state) {
485 - if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
486 - if (state) {
487 - if (state.userid) { msg.userid = state.userid; }
488 - if (state.username) { msg.username = state.username; }
489 - if (state.sessionid) { msg.sessionid = state.sessionid; }
490 - if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
491 - }
492 - mesh.SendCommand(msg);
533 +// Add to the server event log, use internationalized events
534 +function MeshServerLogEx(id, args, msg, state)
535 +{
536 + var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
537 + if (state)
538 + {
539 + if (state.userid) { msg.userid = state.userid; }
540 + if (state.username) { msg.username = state.username; }
541 + if (state.sessionid) { msg.sessionid = state.sessionid; }
542 + if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
543 }
544 + mesh.SendCommand(msg);
545 +}
546
495 - // Add to the server event log, use internationalized events
496 - function MeshServerLogEx(id, args, msg, state) {
497 - var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
498 - if (state) {
499 - if (state.userid) { msg.userid = state.userid; }
500 - if (state.username) { msg.username = state.username; }
501 - if (state.sessionid) { msg.sessionid = state.sessionid; }
502 - if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
547 +// Import libraries
548 +db = require('SimpleDataStore').Shared();
549 +sha = require('SHA256Stream');
550 +mesh = require('MeshAgent');
551 +childProcess = require('child_process');
552 +
553 +if (mesh.hasKVM == 1)
554 +{ // if the agent is compiled with KVM support
555 + // Check if this computer supports a desktop
556 + try
557 + {
558 + if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
559 + {
560 + meshCoreObj.caps |= 1; meshCoreObjChanged();
561 + } else if (process.platform == 'linux' || process.platform == 'freebsd')
562 + {
563 + require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); });
564 }
504 - mesh.SendCommand(msg);
505 - }
565 + } catch (e) { }
566 +}
567 +mesh.DAIPC = obj.DAIPC;
568
507 - // Import libraries
508 - db = require('SimpleDataStore').Shared();
509 - sha = require('SHA256Stream');
510 - mesh = require('MeshAgent');
511 - childProcess = require('child_process');
512 - if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
513 - // Check if this computer supports a desktop
514 - try {
515 - if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) {
516 - meshCoreObj.caps |= 1; meshCoreObjChanged();
517 - } else if (process.platform == 'linux' || process.platform == 'freebsd') {
518 - require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); });
519 - }
520 - } catch (e) { }
521 - }
522 - mesh.DAIPC = obj.DAIPC;
569 +/*
570 +// Try to load up the network monitor
571 +try {
572 + networkMonitor = require('NetworkMonitor');
573 + networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
574 + networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
575 + networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
576 +} catch (e) { networkMonitor = null; }
577 +*/
578
524 - /*
525 - // Try to load up the network monitor
526 - try {
527 - networkMonitor = require('NetworkMonitor');
528 - networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
529 - networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
530 - networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
531 - } catch (e) { networkMonitor = null; }
532 - */
579 +// Fetch the SMBios Tables
580 +var SMBiosTables = null;
581 +var SMBiosTablesRaw = null;
582 +try
583 +{
584 + var SMBiosModule = null;
585 + try { SMBiosModule = require('smbios'); } catch (e) { }
586 + if (SMBiosModule != null)
587 + {
588 + SMBiosModule.get(function (data)
589 + {
590 + if (data != null)
591 + {
592 + SMBiosTablesRaw = data;
593 + SMBiosTables = require('smbios').parse(data)
594 + if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
595
534 - // Fetch the SMBios Tables
535 - var SMBiosTables = null;
536 - var SMBiosTablesRaw = null;
537 - try {
538 - var SMBiosModule = null;
539 - try { SMBiosModule = require('smbios'); } catch (e) { }
540 - if (SMBiosModule != null) {
541 - SMBiosModule.get(function (data) {
542 - if (data != null) {
543 - SMBiosTablesRaw = data;
544 - SMBiosTables = require('smbios').parse(data)
545 - if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
546 -
547 - // If SMBios tables say that Intel AMT is present, try to connect MEI
548 - if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) {
549 - var amtmodule = require('amt-manage');
550 - amt = new amtmodule(mesh, db, false);
551 - amt.on('portBinding_LMS', function (map) { mesh.SendCommand({ action: 'lmsinfo', value: { ports: map.keys() } }); });
552 - amt.on('stateChange_LMS', function (v) { if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; } meshCoreObj.intelamt.microlms = v; meshCoreObjChanged(); }); // 0 = Disabled, 1 = Connecting, 2 = Connected
553 - amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } } // MEI State
554 - amt.reset();
555 - }
596 + // If SMBios tables say that Intel AMT is present, try to connect MEI
597 + if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true))
598 + {
599 + var amtmodule = require('amt-manage');
600 + amt = new amtmodule(mesh, db, false);
601 + amt.on('portBinding_LMS', function (map) { mesh.SendCommand({ action: 'lmsinfo', value: { ports: map.keys() } }); });
602 + amt.on('stateChange_LMS', function (v) { if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; } meshCoreObj.intelamt.microlms = v; meshCoreObjChanged(); }); // 0 = Disabled, 1 = Connecting, 2 = Connected
603 + amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } } // MEI State
604 + amt.reset();
605 }
557 - });
558 - }
559 - } catch (e) { sendConsoleText("ex1: " + e); }
560 -
561 - // Try to load up the WIFI scanner
562 - try {
563 - var wifiScannerLib = require('wifi-scanner');
564 - wifiScanner = new wifiScannerLib();
565 - wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
566 - } catch (e) { wifiScannerLib = null; wifiScanner = null; }
567 -
568 - // Get our location (lat/long) using our public IP address
569 - var getIpLocationDataExInProgress = false;
570 - var getIpLocationDataExCounts = [0, 0];
571 - function getIpLocationDataEx(func) {
572 - if (getIpLocationDataExInProgress == true) { return false; }
573 - try {
574 - getIpLocationDataExInProgress = true;
575 - getIpLocationDataExCounts[0]++;
576 - var options = http.parseUri("http://ipinfo.io/json");
577 - options.method = 'GET';
578 - http.request(options, function (resp) {
579 - if (resp.statusCode == 200) {
580 - var geoData = '';
581 - resp.data = function (geoipdata) { geoData += geoipdata; };
582 - resp.end = function () {
583 - var location = null;
584 - try {
585 - if (typeof geoData == 'string') {
586 - var result = JSON.parse(geoData);
587 - if (result.ip && result.loc) { location = result; }
588 - }
589 - } catch (e) { }
590 - if (func) { getIpLocationDataExCounts[1]++; func(location); }
591 - }
592 - } else
593 - { func(null); }
594 - getIpLocationDataExInProgress = false;
595 - }).end();
596 - return true;
597 - }
598 - catch (e) { return false; }
606 + }
607 + });
608 }
600 -
601 - // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
602 - function clearGatewayMac(str) {
603 - if (str == null) return null;
604 - var x = JSON.parse(str);
605 - for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
606 - return JSON.stringify(x);
609 +} catch (e) { sendConsoleText("ex1: " + e); }
610 +
611 +// Try to load up the WIFI scanner
612 +try
613 +{
614 + var wifiScannerLib = require('wifi-scanner');
615 + wifiScanner = new wifiScannerLib();
616 + wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
617 +} catch (e) { wifiScannerLib = null; wifiScanner = null; }
618 +
619 +// Get our location (lat/long) using our public IP address
620 +var getIpLocationDataExInProgress = false;
621 +var getIpLocationDataExCounts = [0, 0];
622 +function getIpLocationDataEx(func)
623 +{
624 + if (getIpLocationDataExInProgress == true) { return false; }
625 + try
626 + {
627 + getIpLocationDataExInProgress = true;
628 + getIpLocationDataExCounts[0]++;
629 + var options = http.parseUri("http://ipinfo.io/json");
630 + options.method = 'GET';
631 + http.request(options, function (resp)
632 + {
633 + if (resp.statusCode == 200)
634 + {
635 + var geoData = '';
636 + resp.data = function (geoipdata) { geoData += geoipdata; };
637 + resp.end = function ()
638 + {
639 + var location = null;
640 + try
641 + {
642 + if (typeof geoData == 'string')
643 + {
644 + var result = JSON.parse(geoData);
645 + if (result.ip && result.loc) { location = result; }
646 + }
647 + } catch (e) { }
648 + if (func) { getIpLocationDataExCounts[1]++; func(location); }
649 + }
650 + } else
651 + { func(null); }
652 + getIpLocationDataExInProgress = false;
653 + }).end();
654 + return true;
655 }
656 + catch (e) { return false; }
657 +}
658
609 - function getIpLocationData(func) {
610 - // Get the location information for the cache if possible
611 - var publicLocationInfo = db.Get('publicLocationInfo');
612 - if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
613 - if (publicLocationInfo == null) {
614 - // Nothing in the cache, fetch the data
615 - getIpLocationDataEx(function (locationData) {
616 - if (locationData != null) {
659 +// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
660 +function clearGatewayMac(str)
661 +{
662 + if (str == null) return null;
663 + var x = JSON.parse(str);
664 + for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
665 + return JSON.stringify(x);
666 +}
667 +
668 +function getIpLocationData(func)
669 +{
670 + // Get the location information for the cache if possible
671 + var publicLocationInfo = db.Get('publicLocationInfo');
672 + if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
673 + if (publicLocationInfo == null)
674 + {
675 + // Nothing in the cache, fetch the data
676 + getIpLocationDataEx(function (locationData)
677 + {
678 + if (locationData != null)
679 + {
680 + publicLocationInfo = {};
681 + publicLocationInfo.netInfoStr = lastNetworkInfo;
682 + publicLocationInfo.locationData = locationData;
683 + var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
684 + if (func) func(locationData); // Report the new location
685 + }
686 + else
687 + {
688 + if (func) func(null); // Report no location
689 + }
690 + });
691 + }
692 + else
693 + {
694 + // Check the cache
695 + if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo))
696 + {
697 + // Cache match
698 + if (func) func(publicLocationInfo.locationData);
699 + }
700 + else
701 + {
702 + // Cache mismatch
703 + getIpLocationDataEx(function (locationData)
704 + {
705 + if (locationData != null)
706 + {
707 publicLocationInfo = {};
708 publicLocationInfo.netInfoStr = lastNetworkInfo;
709 publicLocationInfo.locationData = locationData;
710 var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
711 if (func) func(locationData); // Report the new location
622 - } else {
623 - if (func) func(null); // Report no location
712 + }
713 + else
714 + {
715 + if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
716 }
717 });
626 - } else {
627 - // Check the cache
628 - if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
629 - // Cache match
630 - if (func) func(publicLocationInfo.locationData);
631 - } else {
632 - // Cache mismatch
633 - getIpLocationDataEx(function (locationData) {
634 - if (locationData != null) {
635 - publicLocationInfo = {};
636 - publicLocationInfo.netInfoStr = lastNetworkInfo;
637 - publicLocationInfo.locationData = locationData;
638 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
639 - if (func) func(locationData); // Report the new location
640 - } else {
641 - if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
642 - }
643 - });
644 - }
718 }
719 }
720 +}
721
648 - // Polyfill String.endsWith
649 - if (!String.prototype.endsWith) {
650 - String.prototype.endsWith = function (searchString, position) {
651 - var subjectString = this.toString();
652 - if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
653 - position -= searchString.length;
654 - var lastIndex = subjectString.lastIndexOf(searchString, position);
655 - return lastIndex !== -1 && lastIndex === position;
656 - };
657 - }
722 +// Polyfill String.endsWith
723 +if (!String.prototype.endsWith)
724 +{
725 + String.prototype.endsWith = function (searchString, position)
726 + {
727 + var subjectString = this.toString();
728 + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
729 + position -= searchString.length;
730 + var lastIndex = subjectString.lastIndexOf(searchString, position);
731 + return lastIndex !== -1 && lastIndex === position;
732 + };
733 +}
734
659 - // Polyfill path.join
660 - obj.path = {
661 - join: function () {
735 +// Polyfill path.join
736 +obj.path =
737 + {
738 + join: function ()
739 + {
740 var x = [];
663 - for (var i in arguments) {
741 + for (var i in arguments)
742 + {
743 var w = arguments[i];
665 - if (w != null) {
744 + if (w != null)
745 + {
746 while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
667 - if (i != 0) {
747 + if (i != 0)
748 + {
749 while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
750 }
751 x.push(w);
@@ -675,1198 +756,1385 @@ function createMeshCore(agent) {
756 }
757 };
758
678 - // Replace a string with a number if the string is an exact number
679 - function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
759 +// Replace a string with a number if the string is an exact number
760 +function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
761
681 - // Convert decimal to hex
682 - function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
762 +// Convert decimal to hex
763 +function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
764
684 - // Convert a raw string to a hex string
685 - function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
765 +// Convert a raw string to a hex string
766 +function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
767
687 - // Convert a buffer into a string
688 - function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
768 +// Convert a buffer into a string
769 +function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
770
690 - // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
691 - function hex2rstr(d) {
692 - if (typeof d != "string" || d.length == 0) return '';
693 - var r = '', m = ('' + d).match(/../g), t;
694 - while (t = m.shift()) r += String.fromCharCode('0x' + t);
695 - return r
696 - }
771 +// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
772 +function hex2rstr(d)
773 +{
774 + if (typeof d != "string" || d.length == 0) return '';
775 + var r = '', m = ('' + d).match(/../g), t;
776 + while (t = m.shift()) r += String.fromCharCode('0x' + t);
777 + return r
778 +}
779
698 - // Convert an object to string with all functions
699 - function objToString(x, p, pad, ret) {
700 - if (ret == undefined) ret = '';
701 - if (p == undefined) p = 0;
702 - if (x == null) { return '[null]'; }
703 - if (p > 8) { return '[...]'; }
704 - if (x == undefined) { return '[undefined]'; }
705 - if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
706 - if (typeof x == 'buffer') { return '[buffer]'; }
707 - if (typeof x != 'object') { return x; }
708 - var r = '{' + (ret ? '\r\n' : ' ');
709 - for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
710 - return r + addPad(p, pad) + '}';
711 - }
780 +// Convert an object to string with all functions
781 +function objToString(x, p, pad, ret)
782 +{
783 + if (ret == undefined) ret = '';
784 + if (p == undefined) p = 0;
785 + if (x == null) { return '[null]'; }
786 + if (p > 8) { return '[...]'; }
787 + if (x == undefined) { return '[undefined]'; }
788 + if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
789 + if (typeof x == 'buffer') { return '[buffer]'; }
790 + if (typeof x != 'object') { return x; }
791 + var r = '{' + (ret ? '\r\n' : ' ');
792 + for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
793 + return r + addPad(p, pad) + '}';
794 +}
795
713 - // Return p number of spaces
714 - function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
796 +// Return p number of spaces
797 +function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
798
716 - // Split a string taking into account the quoats. Used for command line parsing
717 - function splitArgs(str) {
718 - var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
719 - do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
720 - return myArray;
721 - }
799 +// Split a string taking into account the quoats. Used for command line parsing
800 +function splitArgs(str)
801 +{
802 + var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
803 + do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
804 + return myArray;
805 +}
806
723 - // Parse arguments string array into an object
724 - function parseArgs(argv) {
725 - var results = { '_': [] }, current = null;
726 - for (var i = 1, len = argv.length; i < len; i++) {
727 - var x = argv[i];
728 - if (x.length > 2 && x[0] == '-' && x[1] == '-') {
729 - if (current != null) { results[current] = true; }
730 - current = x.substring(2);
731 - } else {
732 - if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
733 - }
807 +// Parse arguments string array into an object
808 +function parseArgs(argv)
809 +{
810 + var results = { '_': [] }, current = null;
811 + for (var i = 1, len = argv.length; i < len; i++)
812 + {
813 + var x = argv[i];
814 + if (x.length > 2 && x[0] == '-' && x[1] == '-')
815 + {
816 + if (current != null) { results[current] = true; }
817 + current = x.substring(2);
818 + } else
819 + {
820 + if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
821 }
735 - if (current != null) { results[current] = true; }
736 - return results;
822 }
823 + if (current != null) { results[current] = true; }
824 + return results;
825 +}
826
739 - // Get server target url with a custom path
740 - function getServerTargetUrl(path) {
741 - var x = mesh.ServerUrl;
742 - //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
743 - if (x == null) { return null; }
744 - if (path == null) { path = ''; }
745 - x = http.parseUri(x);
746 - if (x == null) return null;
747 - return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
748 - }
827 +// Get server target url with a custom path
828 +function getServerTargetUrl(path)
829 +{
830 + var x = mesh.ServerUrl;
831 + //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
832 + if (x == null) { return null; }
833 + if (path == null) { path = ''; }
834 + x = http.parseUri(x);
835 + if (x == null) return null;
836 + return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
837 +}
838
750 - // Get server url. If the url starts with "*/..." change it, it not use the url as is.
751 - function getServerTargetUrlEx(url) {
752 - if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
753 - return url;
754 - }
839 +// Get server url. If the url starts with "*/..." change it, it not use the url as is.
840 +function getServerTargetUrlEx(url)
841 +{
842 + if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
843 + return url;
844 +}
845
756 - // Send a wake-on-lan packet
757 - function sendWakeOnLan(hexMac) {
758 - hexMac = hexMac.split(':').join('');
759 - var count = 0;
760 - try {
761 - var interfaces = require('os').networkInterfaces();
762 - var magic = 'FFFFFFFFFFFF';
763 - for (var x = 1; x <= 16; ++x) { magic += hexMac; }
764 - var magicbin = Buffer.from(magic, 'hex');
765 -
766 - for (var adapter in interfaces) {
767 - if (interfaces.hasOwnProperty(adapter)) {
768 - for (var i = 0; i < interfaces[adapter].length; ++i) {
769 - var addr = interfaces[adapter][i];
770 - if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
771 - try {
772 - var socket = require('dgram').createSocket({ type: 'udp4' });
773 - socket.bind({ address: addr.address });
774 - socket.setBroadcast(true);
775 - socket.setMulticastInterface(addr.address);
776 - socket.setMulticastTTL(1);
777 - socket.send(magicbin, 7, '255.255.255.255');
778 - socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
779 - count++;
780 - }
781 - catch (e) { }
846 +// Send a wake-on-lan packet
847 +function sendWakeOnLan(hexMac)
848 +{
849 + hexMac = hexMac.split(':').join('');
850 + var count = 0;
851 + try
852 + {
853 + var interfaces = require('os').networkInterfaces();
854 + var magic = 'FFFFFFFFFFFF';
855 + for (var x = 1; x <= 16; ++x) { magic += hexMac; }
856 + var magicbin = Buffer.from(magic, 'hex');
857 +
858 + for (var adapter in interfaces)
859 + {
860 + if (interfaces.hasOwnProperty(adapter))
861 + {
862 + for (var i = 0; i < interfaces[adapter].length; ++i)
863 + {
864 + var addr = interfaces[adapter][i];
865 + if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00'))
866 + {
867 + try
868 + {
869 + var socket = require('dgram').createSocket({ type: 'udp4' });
870 + socket.bind({ address: addr.address });
871 + socket.setBroadcast(true);
872 + socket.setMulticastInterface(addr.address);
873 + socket.setMulticastTTL(1);
874 + socket.send(magicbin, 7, '255.255.255.255');
875 + socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
876 + count++;
877 }
878 + catch (e) { }
879 }
880 }
881 }
786 - } catch (e) { }
787 - return count;
788 - }
882 + }
883 + } catch (e) { }
884 + return count;
885 +}
886
790 - // Handle a mesh agent command
791 - function handleServerCommand(data) {
792 - if (typeof data == 'object') {
793 - // If this is a console command, parse it and call the console handler
794 - switch (data.action) {
795 - case 'agentupdate':
796 - agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash, sessionid: data.sessionid });
797 - break;
798 - case 'msg': {
799 - switch (data.type) {
800 - case 'console': { // Process a console command
801 - if (data.value && data.sessionid) {
802 - MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
803 - var args = splitArgs(data.value);
804 - processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
805 - }
806 - break;
887 +// Handle a mesh agent command
888 +function handleServerCommand(data)
889 +{
890 + if (typeof data == 'object')
891 + {
892 + // If this is a console command, parse it and call the console handler
893 + switch (data.action)
894 + {
895 + case 'agentupdate':
896 + agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash, sessionid: data.sessionid });
897 + break;
898 + case 'msg': {
899 + switch (data.type)
900 + {
901 + case 'console': { // Process a console command
902 + if (data.value && data.sessionid)
903 + {
904 + MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
905 + var args = splitArgs(data.value);
906 + processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
907 }
808 - case 'tunnel': {
809 - if (data.value != null) { // Process a new tunnel connection request
810 - // Create a new tunnel object
811 - var xurl = getServerTargetUrlEx(data.value);
812 - if (xurl != null) {
813 - xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
814 - var woptions = http.parseUri(xurl);
815 - woptions.perMessageDeflate = false;
816 - if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
817 -
818 - // Perform manual server TLS certificate checking based on the certificate hash given by the server.
819 - woptions.rejectUnauthorized = 0;
820 - woptions.checkServerIdentity = function checkServerIdentity(certs) {
821 - // If the tunnel certificate matches the control channel certificate, accept the connection
822 - try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
823 - try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
824 - // Check that the certificate is the one expected by the server, fail if not.
825 - if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
826 - }
827 - woptions.checkServerIdentity.servertlshash = data.servertlshash;
828 -
829 - //sendConsoleText(JSON.stringify(woptions));
830 - //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
831 - var tunnel = http.request(woptions);
832 - tunnel.upgrade = onTunnelUpgrade;
833 - tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
834 - tunnel.sessionid = data.sessionid;
835 - tunnel.rights = data.rights;
836 - tunnel.consent = data.consent;
837 - tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
838 - tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
839 - tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
840 - tunnel.userid = data.userid;
841 - tunnel.remoteaddr = data.remoteaddr;
842 - tunnel.state = 0;
843 - tunnel.url = xurl;
844 - tunnel.protocol = 0;
845 - tunnel.soptions = data.soptions;
846 - tunnel.tcpaddr = data.tcpaddr;
847 - tunnel.tcpport = data.tcpport;
848 - tunnel.udpaddr = data.udpaddr;
849 - tunnel.udpport = data.udpport;
850 - tunnel.end();
851 - // Put the tunnel in the tunnels list
852 - var index = nextTunnelIndex++;
853 - tunnel.index = index;
854 - tunnels[index] = tunnel;
855 -
856 - //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
908 + break;
909 + }
910 + case 'tunnel': {
911 + if (data.value != null)
912 + { // Process a new tunnel connection request
913 + // Create a new tunnel object
914 + var xurl = getServerTargetUrlEx(data.value);
915 + if (xurl != null)
916 + {
917 + xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
918 + var woptions = http.parseUri(xurl);
919 + woptions.perMessageDeflate = false;
920 + if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
921 +
922 + // Perform manual server TLS certificate checking based on the certificate hash given by the server.
923 + woptions.rejectUnauthorized = 0;
924 + woptions.checkServerIdentity = function checkServerIdentity(certs)
925 + {
926 + // If the tunnel certificate matches the control channel certificate, accept the connection
927 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
928 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
929 + // Check that the certificate is the one expected by the server, fail if not.
930 + if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
931 }
932 + woptions.checkServerIdentity.servertlshash = data.servertlshash;
933 +
934 + //sendConsoleText(JSON.stringify(woptions));
935 + //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
936 + var tunnel = http.request(woptions);
937 + tunnel.upgrade = onTunnelUpgrade;
938 + tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
939 + tunnel.sessionid = data.sessionid;
940 + tunnel.rights = data.rights;
941 + tunnel.consent = data.consent;
942 + tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
943 + tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
944 + tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
945 + tunnel.userid = data.userid;
946 + tunnel.remoteaddr = data.remoteaddr;
947 + tunnel.state = 0;
948 + tunnel.url = xurl;
949 + tunnel.protocol = 0;
950 + tunnel.soptions = data.soptions;
951 + tunnel.tcpaddr = data.tcpaddr;
952 + tunnel.tcpport = data.tcpport;
953 + tunnel.udpaddr = data.udpaddr;
954 + tunnel.udpport = data.udpport;
955 + tunnel.end();
956 + // Put the tunnel in the tunnels list
957 + var index = nextTunnelIndex++;
958 + tunnel.index = index;
959 + tunnels[index] = tunnel;
960 +
961 + //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
962 }
859 - break;
860 - }
861 - case 'messagebox': {
862 - // Display a message box
863 - if (data.title && data.msg) {
864 - MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
865 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
866 - try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
867 - }
868 - break;
869 - }
870 - case 'ps': {
871 - // Return the list of running processes
872 - if (data.sessionid) {
873 - processManager.getProcesses(function (plist) {
874 - mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
875 - });
876 - }
877 - break;
878 - }
879 - case 'pskill': {
880 - // Kill a process
881 - if (data.value) {
882 - MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
883 - try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
884 - }
885 - break;
963 }
887 - case 'services': {
888 - // Return the list of installed services
889 - var services = null;
890 - try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
891 - if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
892 - break;
893 - }
894 - case 'serviceStop': {
895 - // Stop a service
896 - try {
897 - var service = require('service-manager').manager.getService(data.serviceName);
898 - if (service != null) { service.stop(); }
899 - } catch (e) { }
900 - break;
964 + break;
965 + }
966 + case 'messagebox': {
967 + // Display a message box
968 + if (data.title && data.msg)
969 + {
970 + MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
971 + data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
972 + try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
973 }
902 - case 'serviceStart': {
903 - // Start a service
904 - try {
905 - var service = require('service-manager').manager.getService(data.serviceName);
906 - if (service != null) { service.start(); }
907 - } catch (e) { }
908 - break;
974 + break;
975 + }
976 + case 'ps': {
977 + // Return the list of running processes
978 + if (data.sessionid)
979 + {
980 + processManager.getProcesses(function (plist)
981 + {
982 + mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
983 + });
984 }
910 - case 'serviceRestart': {
911 - // Restart a service
912 - try {
913 - var service = require('service-manager').manager.getService(data.serviceName);
914 - if (service != null) { service.restart(); }
915 - } catch (e) { }
916 - break;
985 + break;
986 + }
987 + case 'pskill': {
988 + // Kill a process
989 + if (data.value)
990 + {
991 + MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
992 + try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
993 }
918 - case 'deskBackground':
994 + break;
995 + }
996 + case 'services': {
997 + // Return the list of installed services
998 + var services = null;
999 + try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
1000 + if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
1001 + break;
1002 + }
1003 + case 'serviceStop': {
1004 + // Stop a service
1005 + try
1006 + {
1007 + var service = require('service-manager').manager.getService(data.serviceName);
1008 + if (service != null) { service.stop(); }
1009 + } catch (e) { }
1010 + break;
1011 + }
1012 + case 'serviceStart': {
1013 + // Start a service
1014 + try
1015 + {
1016 + var service = require('service-manager').manager.getService(data.serviceName);
1017 + if (service != null) { service.start(); }
1018 + } catch (e) { }
1019 + break;
1020 + }
1021 + case 'serviceRestart': {
1022 + // Restart a service
1023 + try
1024 + {
1025 + var service = require('service-manager').manager.getService(data.serviceName);
1026 + if (service != null) { service.restart(); }
1027 + } catch (e) { }
1028 + break;
1029 + }
1030 + case 'deskBackground':
1031 + {
1032 + // Toggle desktop background
1033 + try
1034 {
920 - // Toggle desktop background
921 - try {
922 - if (process.platform == 'win32') {
923 - var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
924 - var sid = undefined;
925 - if (stype == 1) {
926 - if (require('MeshAgent')._tsid != null) {
927 - stype = 5;
928 - sid = require('MeshAgent')._tsid;
929 - }
1035 + if (process.platform == 'win32')
1036 + {
1037 + var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
1038 + var sid = undefined;
1039 + if (stype == 1)
1040 + {
1041 + if (require('MeshAgent')._tsid != null)
1042 + {
1043 + stype = 5;
1044 + sid = require('MeshAgent')._tsid;
1045 }
931 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
932 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
933 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
934 - child.stderr.on('data', function () { });
935 - child.waitExit();
936 - var current = child.stdout.str.trim();
937 - if (current != '') { require('MeshAgent')._wallpaper = current; }
938 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
939 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
940 - child.stderr.on('data', function () { });
941 - child.waitExit();
942 - } else {
943 - var id = require('user-sessions').consoleUid();
944 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
945 - if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
946 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
1046 }
948 - } catch (e) {
949 - sendConsoleText(e);
1047 + var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
1048 + var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
1049 + child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
1050 + child.stderr.on('data', function () { });
1051 + child.waitExit();
1052 + var current = child.stdout.str.trim();
1053 + if (current != '') { require('MeshAgent')._wallpaper = current; }
1054 + child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
1055 + child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
1056 + child.stderr.on('data', function () { });
1057 + child.waitExit();
1058 + } else
1059 + {
1060 + var id = require('user-sessions').consoleUid();
1061 + var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
1062 + if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
1063 + require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
1064 }
951 - break;
952 - }
953 - case 'openUrl': {
954 - // Open a local web browser and return success/fail
955 - MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
956 - sendConsoleText("OpenURL: " + data.url);
957 - if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
958 - break;
959 - }
960 - case 'getclip': {
961 - // Send the load clipboard back to the user
962 - //sendConsoleText('getClip: ' + JSON.stringify(data));
963 - if (require('MeshAgent').isService) {
964 - require('clipboard').dispatchRead().then(function (str) {
965 - if (str) {
966 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
967 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
968 - }
969 - });
970 - } else {
971 - require("clipboard").read().then(function (str) {
972 - if (str) {
973 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
974 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
975 - }
976 - });
1065 + } catch (e)
1066 + {
1067 + sendConsoleText(e);
1068 }
1069 break;
1070 }
980 - case 'setclip': {
981 - // Set the load clipboard to a user value
982 - sendConsoleText('setClip: ' + JSON.stringify(data));
983 - if (typeof data.data == 'string') {
984 - MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
985 - if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
986 - mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
987 - }
988 - break;
1071 + case 'openUrl': {
1072 + // Open a local web browser and return success/fail
1073 + MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1074 + sendConsoleText("OpenURL: " + data.url);
1075 + if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1076 + break;
1077 + }
1078 + case 'getclip': {
1079 + // Send the load clipboard back to the user
1080 + //sendConsoleText('getClip: ' + JSON.stringify(data));
1081 + if (require('MeshAgent').isService)
1082 + {
1083 + require('clipboard').dispatchRead().then(function (str)
1084 + {
1085 + if (str)
1086 + {
1087 + MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
1088 + mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
1089 + }
1090 + });
1091 + } else
1092 + {
1093 + require("clipboard").read().then(function (str)
1094 + {
1095 + if (str)
1096 + {
1097 + MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
1098 + mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
1099 + }
1100 + });
1101 }
990 - case 'userSessions': {
991 - // Send back current user sessions list, this is Windows only.
992 - //sendConsoleText('userSessions: ' + JSON.stringify(data));
993 - if (process.platform != 'win32') break;
994 - var p = require('user-sessions').enumerateUsers();
995 - p.sessionid = data.sessionid;
996 - p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
997 - break;
1102 + break;
1103 + }
1104 + case 'setclip': {
1105 + // Set the load clipboard to a user value
1106 + sendConsoleText('setClip: ' + JSON.stringify(data));
1107 + if (typeof data.data == 'string')
1108 + {
1109 + MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
1110 + if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
1111 + mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
1112 }
999 - case 'cpuinfo':
1000 - // CPU & memory utilization
1001 - var cpuuse = require('sysinfo').cpuUtilization();
1002 - cpuuse.sessionid = data.sessionid;
1003 - cpuuse.tag = data.tag;
1004 - cpuuse.then(function (data)
1005 - {
1006 - mesh.SendCommand(JSON.stringify(
1007 - {
1008 - action: 'msg',
1009 - type: 'cpuinfo',
1010 - cpu: data,
1011 - memory: require('sysinfo').memUtilization(),
1012 - thermals: require('sysinfo').thermals==null?[]:require('sysinfo').thermals(),
1013 - sessionid: this.sessionid,
1014 - tag: this.tag
1015 - }));
1016 - }, function (ex) { });
1017 - break;
1018 - case 'localapp':
1019 - // Send a message to a local application
1020 - sendConsoleText('localappMsg: ' + data.appid + ', ' + JSON.stringify(data.value));
1021 - if (data.appid != null) { sendToRegisteredApp(data.appid, data.value); } else { broadcastToRegisteredApps(data.value); }
1022 - break;
1023 - default:
1024 - // Unknown action, ignore it.
1025 - break;
1113 + break;
1114 }
1027 - break;
1028 - }
1029 - case 'acmactivate': {
1030 - if (amt != null) {
1031 - MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
1032 - amt.setAcmResponse(data);
1115 + case 'userSessions': {
1116 + // Send back current user sessions list, this is Windows only.
1117 + //sendConsoleText('userSessions: ' + JSON.stringify(data));
1118 + if (process.platform != 'win32') break;
1119 + var p = require('user-sessions').enumerateUsers();
1120 + p.sessionid = data.sessionid;
1121 + p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
1122 + break;
1123 }
1034 - break;
1124 + case 'cpuinfo':
1125 + // CPU & memory utilization
1126 + var cpuuse = require('sysinfo').cpuUtilization();
1127 + cpuuse.sessionid = data.sessionid;
1128 + cpuuse.tag = data.tag;
1129 + cpuuse.then(function (data)
1130 + {
1131 + mesh.SendCommand(JSON.stringify(
1132 + {
1133 + action: 'msg',
1134 + type: 'cpuinfo',
1135 + cpu: data,
1136 + memory: require('sysinfo').memUtilization(),
1137 + thermals: require('sysinfo').thermals == null ? [] : require('sysinfo').thermals(),
1138 + sessionid: this.sessionid,
1139 + tag: this.tag
1140 + }));
1141 + }, function (ex) { });
1142 + break;
1143 + case 'localapp':
1144 + // Send a message to a local application
1145 + sendConsoleText('localappMsg: ' + data.appid + ', ' + JSON.stringify(data.value));
1146 + if (data.appid != null) { sendToRegisteredApp(data.appid, data.value); } else { broadcastToRegisteredApps(data.value); }
1147 + break;
1148 + default:
1149 + // Unknown action, ignore it.
1150 + break;
1151 }
1036 - case 'wakeonlan': {
1037 - // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
1038 - //sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
1039 - for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
1040 - break;
1152 + break;
1153 + }
1154 + case 'acmactivate': {
1155 + if (amt != null)
1156 + {
1157 + MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
1158 + amt.setAcmResponse(data);
1159 }
1042 - case 'runcommands': {
1043 - if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
1044 - sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
1045 -
1046 - // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
1047 - var options = {};
1048 - if (data.runAsUser > 0) {
1049 - try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
1050 - options.type = require('child_process').SpawnTypes.TERM;
1051 - }
1052 - if (data.runAsUser == 2) {
1053 - if (options.uid == null) break;
1054 - if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1055 - }
1160 + break;
1161 + }
1162 + case 'wakeonlan': {
1163 + // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
1164 + //sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
1165 + for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
1166 + break;
1167 + }
1168 + case 'runcommands': {
1169 + if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
1170 + sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
1171
1057 - if (process.platform == 'win32') {
1058 - if (data.type == 1) {
1059 - // Windows command shell
1060 - mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1061 - mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1062 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1063 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1064 - mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1065 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1066 - } else if (data.type == 2) {
1067 - // Windows Powershell
1068 - mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1069 - mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
1070 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1071 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1072 - mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1073 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1074 - }
1075 - } else if (data.type == 3) {
1076 - // Linux shell
1077 - mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1172 + // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
1173 + var options = {};
1174 + if (data.runAsUser > 0)
1175 + {
1176 + try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
1177 + options.type = require('child_process').SpawnTypes.TERM;
1178 + }
1179 + if (data.runAsUser == 2)
1180 + {
1181 + if (options.uid == null) break;
1182 + if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1183 + }
1184 +
1185 + if (process.platform == 'win32')
1186 + {
1187 + if (data.type == 1)
1188 + {
1189 + // Windows command shell
1190 + mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1191 mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1192 mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1193 mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1081 - mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
1194 + mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1195 + mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1196 + } else if (data.type == 2)
1197 + {
1198 + // Windows Powershell
1199 + mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1200 + mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
1201 + mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1202 + mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1203 + mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1204 mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1205 }
1084 - break;
1085 - }
1086 - case 'uninstallagent':
1087 - // Uninstall this agent
1088 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
1089 - try {
1090 - agentName = require('MeshAgent').serviceName;
1091 - }
1092 - catch (x) {
1093 - }
1094 -
1095 - if (require('service-manager').manager.getService(agentName).isMe()) {
1096 - try { diagnosticAgent_uninstall(); } catch (e) { }
1097 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1098 - 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 });
1099 - }
1100 - break;
1101 - case 'poweraction': {
1102 - // Server telling us to execute a power action
1103 - if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
1104 - var forced = 0;
1105 - if (data.forced == 1) { forced = 1; }
1106 - data.actiontype = parseInt(data.actiontype);
1107 - MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
1108 - sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
1109 - var r = mesh.ExecPowerState(data.actiontype, forced);
1110 - sendConsoleText("ExecPowerState returned code: " + r);
1111 - }
1112 - break;
1113 - }
1114 - case 'iplocation': {
1115 - // 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
1116 - getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
1117 - break;
1118 - }
1119 - case 'toast': {
1120 - // Display a toast message
1121 - if (data.title && data.msg) {
1122 - MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1123 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1124 - try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
1125 - }
1126 - break;
1206 + } else if (data.type == 3)
1207 + {
1208 + // Linux shell
1209 + mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1210 + mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1211 + mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1212 + mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1213 + mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
1214 + mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1215 }
1128 - case 'openUrl': {
1129 - // Open a local web browser and return success/fail
1130 - //sendConsoleText('OpenURL: ' + data.url);
1131 - MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1132 - if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1133 - break;
1216 + break;
1217 + }
1218 + case 'uninstallagent':
1219 + // Uninstall this agent
1220 + var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
1221 + try
1222 + {
1223 + agentName = require('MeshAgent').serviceName;
1224 }
1135 - case 'amtconfig': {
1136 - // Perform Intel AMT activation and/or configuration
1137 - if ((apftunnel != null) || (amt == null) || (typeof data.user != 'string') || (typeof data.pass != 'string')) break;
1138 - amt.getMeiState(15, function (state) {
1139 - if ((apftunnel != null) || (amt == null)) return;
1140 - if ((state == null) || (state.ProvisioningState == null)) return;
1141 - if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
1142 - var apfarg = {
1143 - mpsurl: mesh.ServerUrl.replace('/agent.ashx', '/apf.ashx'),
1144 - mpsuser: data.user, // Agent user name
1145 - mpspass: data.pass, // Encrypted login cookie
1146 - mpskeepalive: 60000,
1147 - clientname: state.OsHostname,
1148 - clientaddress: '127.0.0.1',
1149 - clientuuid: state.UUID,
1150 - conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
1151 - meiState: state // MEI state will be passed to MPS server
1152 - };
1153 - addAmtEvent('LMS tunnel start.');
1154 - apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
1155 - apftunnel.onJsonControl = function (data) {
1156 - if (data.action == 'console') { addAmtEvent(data.msg); } // Add console message to AMT event log
1157 - if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
1158 - if (data.action == 'deactivate') { // Request CCM deactivation
1159 - var amtMeiModule, amtMei;
1160 - try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
1161 - amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); });
1162 - amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
1163 - }
1164 - if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
1165 - }
1166 - apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; }
1167 - try { apftunnel.connect(); } catch (ex) { }
1168 - });
1169 - break;
1225 + catch (x)
1226 + {
1227 }
1171 - case 'getScript': {
1172 - // Received a configuration script from the server
1173 - sendConsoleText('getScript: ' + JSON.stringify(data));
1174 - break;
1228 +
1229 + if (require('service-manager').manager.getService(agentName).isMe())
1230 + {
1231 + try { diagnosticAgent_uninstall(); } catch (e) { }
1232 + var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1233 + 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 });
1234 }
1176 - case 'sysinfo': {
1177 - // Fetch system information
1178 - getSystemInformation(function (results) {
1179 - if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1180 - });
1181 - break;
1235 + break;
1236 + case 'poweraction': {
1237 + // Server telling us to execute a power action
1238 + if ((mesh.ExecPowerState != undefined) && (data.actiontype))
1239 + {
1240 + var forced = 0;
1241 + if (data.forced == 1) { forced = 1; }
1242 + data.actiontype = parseInt(data.actiontype);
1243 + MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
1244 + sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
1245 + var r = mesh.ExecPowerState(data.actiontype, forced);
1246 + sendConsoleText("ExecPowerState returned code: " + r);
1247 }
1183 - case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
1184 - case 'pong': { break; }
1185 - case 'plugin': {
1186 - try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
1187 - break;
1248 + break;
1249 + }
1250 + case 'iplocation': {
1251 + // 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
1252 + getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
1253 + break;
1254 + }
1255 + case 'toast': {
1256 + // Display a toast message
1257 + if (data.title && data.msg)
1258 + {
1259 + MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1260 + data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1261 + try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
1262 }
1189 - case 'coredump':
1190 - // Set the current agent coredump situation.
1191 - if (data.value === true) {
1192 - if (process.platform == 'win32') {
1193 - // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1194 - process.coreDumpLocation = process.execPath.replace('.exe', '.dmp');
1195 - } else {
1196 - process.coreDumpLocation = (process.cwd() != '//') ? (process.cwd() + 'core') : null;
1263 + break;
1264 + }
1265 + case 'openUrl': {
1266 + // Open a local web browser and return success/fail
1267 + //sendConsoleText('OpenURL: ' + data.url);
1268 + MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1269 + if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1270 + break;
1271 + }
1272 + case 'amtconfig': {
1273 + // Perform Intel AMT activation and/or configuration
1274 + if ((apftunnel != null) || (amt == null) || (typeof data.user != 'string') || (typeof data.pass != 'string')) break;
1275 + amt.getMeiState(15, function (state)
1276 + {
1277 + if ((apftunnel != null) || (amt == null)) return;
1278 + if ((state == null) || (state.ProvisioningState == null)) return;
1279 + if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
1280 + var apfarg = {
1281 + mpsurl: mesh.ServerUrl.replace('/agent.ashx', '/apf.ashx'),
1282 + mpsuser: data.user, // Agent user name
1283 + mpspass: data.pass, // Encrypted login cookie
1284 + mpskeepalive: 60000,
1285 + clientname: state.OsHostname,
1286 + clientaddress: '127.0.0.1',
1287 + clientuuid: state.UUID,
1288 + conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
1289 + meiState: state // MEI state will be passed to MPS server
1290 + };
1291 + addAmtEvent('LMS tunnel start.');
1292 + apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
1293 + apftunnel.onJsonControl = function (data)
1294 + {
1295 + if (data.action == 'console') { addAmtEvent(data.msg); } // Add console message to AMT event log
1296 + if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
1297 + if (data.action == 'deactivate')
1298 + { // Request CCM deactivation
1299 + var amtMeiModule, amtMei;
1300 + try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
1301 + amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); });
1302 + amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
1303 }
1198 - } else if (data.value === false) {
1199 - process.coreDumpLocation = null;
1200 - }
1201 - break;
1202 - case 'getcoredump':
1203 - // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
1204 - var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1205 - var coreDumpPath = null;
1206 - if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1207 - if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) {
1208 - try {
1209 - var coredate = fs.statSync(coreDumpPath).mtime;
1210 - var coretime = new Date(coredate).getTime();
1211 - var agenttime = new Date(fs.statSync(process.execPath).mtime).getTime();
1212 - if (coretime > agenttime) { r.exists = (db.Get('CoreDumpTime') != coredate); }
1213 - } catch (ex) { }
1214 - }
1215 - if (r.exists == true) {
1216 - r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); // Hash of current agent
1217 - r.corehashhex = getSHA384FileHash(coreDumpPath).toString('hex'); // Hash of core dump file
1304 + if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
1305 }
1219 - mesh.SendCommand(JSON.stringify(r));
1220 - break;
1221 - case 'meshToolInfo':
1222 - if (data.pipe == true) { delete data.pipe; delete data.action; data.cmd = 'meshToolInfo'; broadcastToRegisteredApps(data); }
1223 - break;
1224 - case 'wget': // Server uses this command to tell the agent to download a file using HTTPS/GET and place it in a given path. This is used for one-to-many file uploads.
1225 - agentFileHttpPendingRequests.push(data);
1226 - serverFetchFile();
1227 - break;
1228 - default:
1229 - // Unknown action, ignore it.
1230 - break;
1306 + apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; }
1307 + try { apftunnel.connect(); } catch (ex) { }
1308 + });
1309 + break;
1310 }
1232 - }
1233 - }
1234 -
1235 - // Agent just get a file from the server and save it locally.
1236 - function serverFetchFile() {
1237 - if ((Object.keys(agentFileHttpRequests).length > 4) || (agentFileHttpPendingRequests.length == 0)) return; // No more than 4 active HTTPS requests to the server.
1238 - var data = agentFileHttpPendingRequests.shift();
1239 - if ((data.overwrite !== true) && fs.existsSync(data.path)) return; // Don't overwrite an existing file.
1240 - if (data.createFolder) { try { fs.mkdirSync(data.folder); } catch (ex) { } } // If requested, create the local folder.
1241 - data.url = 'http' + getServerTargetUrlEx('*/').substring(2);
1242 - var agentFileHttpOptions = http.parseUri(data.url);
1243 - agentFileHttpOptions.path = data.urlpath;
1244 -
1245 - // Perform manual server TLS certificate checking based on the certificate hash given by the server.
1246 - agentFileHttpOptions.rejectUnauthorized = 0;
1247 - agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs) {
1248 - // If the tunnel certificate matches the control channel certificate, accept the connection
1249 - try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
1250 - try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
1251 - // Check that the certificate is the one expected by the server, fail if not.
1252 - if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
1253 - }
1254 - agentFileHttpOptions.checkServerIdentity.servertlshash = data.servertlshash;
1255 -
1256 - if (agentFileHttpOptions == null) return;
1257 - var agentFileHttpRequest = http.request(agentFileHttpOptions,
1258 - function (response) {
1259 - response.xparent = this;
1260 - try {
1261 - response.xfile = fs.createWriteStream(this.xpath, { flags: 'wbN' })
1262 - response.pipe(response.xfile);
1263 - response.end = function () { delete agentFileHttpRequests[this.xparent.xurlpath]; delete this.xparent; serverFetchFile(); }
1264 - } catch (ex) { delete agentFileHttpRequests[this.xurlpath]; delete response.xparent; serverFetchFile(); return; }
1311 + case 'getScript': {
1312 + // Received a configuration script from the server
1313 + sendConsoleText('getScript: ' + JSON.stringify(data));
1314 + break;
1315 }
1266 - );
1267 - agentFileHttpRequest.on('error', function (ex) { delete agentFileHttpRequests[this.xurlpath]; serverFetchFile(); });
1268 - agentFileHttpRequest.end();
1269 - agentFileHttpRequest.xurlpath = data.urlpath;
1270 - agentFileHttpRequest.xpath = data.path;
1271 - agentFileHttpRequests[data.urlpath] = agentFileHttpRequest;
1272 - }
1273 -
1274 - // Called when a file changed in the file system
1275 - /*
1276 - function onFileWatcher(a, b) {
1277 - console.log('onFileWatcher', a, b, this.path);
1278 - var response = getDirectoryInfo(this.path);
1279 - if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
1280 - }
1281 - */
1282 -
1283 - function getSystemInformation(func) {
1284 - try {
1285 - var results = { hardware: require('identifiers').get() }; // Hardware info
1286 - if (results.hardware && results.hardware.windows) {
1287 - // Remove extra entries and things that change quickly
1288 - var x = results.hardware.windows.osinfo;
1289 - try { delete x.FreePhysicalMemory; } catch (e) { }
1290 - try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
1291 - try { delete x.FreeVirtualMemory; } catch (e) { }
1292 - try { delete x.LocalDateTime; } catch (e) { }
1293 - try { delete x.MaxProcessMemorySize; } catch (e) { }
1294 - try { delete x.TotalVirtualMemorySize; } catch (e) { }
1295 - try { delete x.TotalVisibleMemorySize; } catch (e) { }
1296 - try {
1297 - if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1298 - if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1299 - if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1300 - } catch (e) { }
1316 + case 'sysinfo': {
1317 + // Fetch system information
1318 + getSystemInformation(function (results)
1319 + {
1320 + if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1321 + });
1322 + break;
1323 }
1302 - if (process.platform == 'win32')
1303 - {
1304 - results.pendingReboot = require('win-info').pendingReboot(); // Pending reboot
1305 - if (require('identifiers').volumes != null)
1324 + case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
1325 + case 'pong': { break; }
1326 + case 'plugin': {
1327 + try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
1328 + break;
1329 + }
1330 + case 'coredump':
1331 + // Set the current agent coredump situation.
1332 + if (data.value === true)
1333 {
1307 - try
1334 + if (process.platform == 'win32')
1335 {
1309 - results.volumes = require('identifiers').volumes();
1310 - }
1311 - catch (e)
1336 + // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1337 + process.coreDumpLocation = process.execPath.replace('.exe', '.dmp');
1338 + } else
1339 {
1340 + process.coreDumpLocation = (process.cwd() != '//') ? (process.cwd() + 'core') : null;
1341 }
1342 + } else if (data.value === false)
1343 + {
1344 + process.coreDumpLocation = null;
1345 }
1315 - }
1316 -
1317 - /*
1318 - if (process.platform == 'win32') {
1319 - var defragResult = function (r) {
1320 - if (typeof r == 'object') { results[this.callname] = r; }
1321 - if (this.callname == 'defrag') {
1322 - var pr = require('win-info').installedApps(); // Installed apps
1323 - pr.callname = 'installedApps';
1324 - pr.sessionid = data.sessionid;
1325 - pr.then(defragResult, defragResult);
1326 - }
1327 - else {
1328 - results.winpatches = require('win-info').qfe(); // Windows patches
1329 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1330 - func(results);
1331 - }
1332 - }
1333 - var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1334 - pr.callname = 'defrag';
1335 - pr.sessionid = data.sessionid;
1336 - pr.then(defragResult, defragResult);
1337 - } else {
1338 - */
1339 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1340 - func(results);
1341 - //}
1342 - } catch (e) { func(null, e); }
1343 - }
1344 -
1345 - // Get a formated response for a given directory path
1346 - function getDirectoryInfo(reqpath) {
1347 - var response = { path: reqpath, dir: [] };
1348 - if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1349 - // List all the drives in the root, or the root itself
1350 - var results = null;
1351 - try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1352 - if (results != null) {
1353 - for (var i = 0; i < results.length; ++i) {
1354 - var drive = { n: results[i].name, t: 1 };
1355 - if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1356 - response.dir.push(drive);
1346 + break;
1347 + case 'getcoredump':
1348 + // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
1349 + var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1350 + var coreDumpPath = null;
1351 + if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1352 + if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath)))
1353 + {
1354 + try
1355 + {
1356 + var coredate = fs.statSync(coreDumpPath).mtime;
1357 + var coretime = new Date(coredate).getTime();
1358 + var agenttime = new Date(fs.statSync(process.execPath).mtime).getTime();
1359 + if (coretime > agenttime) { r.exists = (db.Get('CoreDumpTime') != coredate); }
1360 + } catch (ex) { }
1361 }
1358 - }
1359 - } else {
1360 - // List all the files and folders in this path
1361 - if (reqpath == '') { reqpath = '/'; }
1362 - var results = null, xpath = obj.path.join(reqpath, '*');
1363 - //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1364 - try { results = fs.readdirSync(xpath); } catch (e) { }
1365 - if (results != null) {
1366 - for (var i = 0; i < results.length; ++i) {
1367 - if ((results[i] != '.') && (results[i] != '..')) {
1368 - var stat = null, p = obj.path.join(reqpath, results[i]);
1369 - //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1370 - try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1371 - if ((stat != null) && (stat != undefined)) {
1372 - if (stat.isDirectory() == true) {
1373 - response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1374 - } else {
1375 - response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1376 - }
1377 - }
1378 - }
1362 + if (r.exists == true)
1363 + {
1364 + r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); // Hash of current agent
1365 + r.corehashhex = getSHA384FileHash(coreDumpPath).toString('hex'); // Hash of core dump file
1366 }
1380 - }
1367 + mesh.SendCommand(JSON.stringify(r));
1368 + break;
1369 + case 'meshToolInfo':
1370 + if (data.pipe == true) { delete data.pipe; delete data.action; data.cmd = 'meshToolInfo'; broadcastToRegisteredApps(data); }
1371 + break;
1372 + case 'wget': // Server uses this command to tell the agent to download a file using HTTPS/GET and place it in a given path. This is used for one-to-many file uploads.
1373 + agentFileHttpPendingRequests.push(data);
1374 + serverFetchFile();
1375 + break;
1376 + default:
1377 + // Unknown action, ignore it.
1378 + break;
1379 }
1382 - return response;
1380 }
1381 +}
1382 +
1383 +// Agent just get a file from the server and save it locally.
1384 +function serverFetchFile()
1385 +{
1386 + if ((Object.keys(agentFileHttpRequests).length > 4) || (agentFileHttpPendingRequests.length == 0)) return; // No more than 4 active HTTPS requests to the server.
1387 + var data = agentFileHttpPendingRequests.shift();
1388 + if ((data.overwrite !== true) && fs.existsSync(data.path)) return; // Don't overwrite an existing file.
1389 + if (data.createFolder) { try { fs.mkdirSync(data.folder); } catch (ex) { } } // If requested, create the local folder.
1390 + data.url = 'http' + getServerTargetUrlEx('*/').substring(2);
1391 + var agentFileHttpOptions = http.parseUri(data.url);
1392 + agentFileHttpOptions.path = data.urlpath;
1393 +
1394 + // Perform manual server TLS certificate checking based on the certificate hash given by the server.
1395 + agentFileHttpOptions.rejectUnauthorized = 0;
1396 + agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs)
1397 + {
1398 + // If the tunnel certificate matches the control channel certificate, accept the connection
1399 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
1400 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
1401 + // Check that the certificate is the one expected by the server, fail if not.
1402 + if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
1403 + }
1404 + agentFileHttpOptions.checkServerIdentity.servertlshash = data.servertlshash;
1405
1385 - // Tunnel callback operations
1386 - function onTunnelUpgrade(response, s, head) {
1387 - this.s = s;
1388 - s.httprequest = this;
1389 - s.end = onTunnelClosed;
1390 - s.tunnel = this;
1391 - s.descriptorMetadata = "MeshAgent_relayTunnel";
1392 -
1393 - if (require('MeshAgent').idleTimeout != null) {
1394 - s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1395 - s.on('timeout', function () {
1396 - this.ping();
1397 - this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1398 - });
1406 + if (agentFileHttpOptions == null) return;
1407 + var agentFileHttpRequest = http.request(agentFileHttpOptions,
1408 + function (response)
1409 + {
1410 + response.xparent = this;
1411 + try
1412 + {
1413 + response.xfile = fs.createWriteStream(this.xpath, { flags: 'wbN' })
1414 + response.pipe(response.xfile);
1415 + response.end = function () { delete agentFileHttpRequests[this.xparent.xurlpath]; delete this.xparent; serverFetchFile(); }
1416 + } catch (ex) { delete agentFileHttpRequests[this.xurlpath]; delete response.xparent; serverFetchFile(); return; }
1417 }
1418 + );
1419 + agentFileHttpRequest.on('error', function (ex) { delete agentFileHttpRequests[this.xurlpath]; serverFetchFile(); });
1420 + agentFileHttpRequest.end();
1421 + agentFileHttpRequest.xurlpath = data.urlpath;
1422 + agentFileHttpRequest.xpath = data.path;
1423 + agentFileHttpRequests[data.urlpath] = agentFileHttpRequest;
1424 +}
1425
1401 - //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1402 -
1403 - if (this.tcpport != null) {
1404 - // This is a TCP relay connection, pause now and try to connect to the target.
1405 - s.pause();
1406 - s.data = onTcpRelayServerTunnelData;
1407 - var connectionOptions = { port: parseInt(this.tcpport) };
1408 - if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1409 - s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1410 - s.tcprelay.peerindex = this.index;
1411 -
1412 - // Add the TCP session to the count and update the server
1413 - if (s.httprequest.userid != null) {
1414 - if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1415 - try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1416 - broadcastSessionsToRegisteredApps();
1426 +// Called when a file changed in the file system
1427 +/*
1428 +function onFileWatcher(a, b) {
1429 + console.log('onFileWatcher', a, b, this.path);
1430 + var response = getDirectoryInfo(this.path);
1431 + if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
1432 +}
1433 +*/
1434 +
1435 +function getSystemInformation(func)
1436 +{
1437 + try
1438 + {
1439 + var results = { hardware: require('identifiers').get() }; // Hardware info
1440 + if (results.hardware && results.hardware.windows)
1441 + {
1442 + // Remove extra entries and things that change quickly
1443 + var x = results.hardware.windows.osinfo;
1444 + try { delete x.FreePhysicalMemory; } catch (e) { }
1445 + try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
1446 + try { delete x.FreeVirtualMemory; } catch (e) { }
1447 + try { delete x.LocalDateTime; } catch (e) { }
1448 + try { delete x.MaxProcessMemorySize; } catch (e) { }
1449 + try { delete x.TotalVirtualMemorySize; } catch (e) { }
1450 + try { delete x.TotalVisibleMemorySize; } catch (e) { }
1451 + try
1452 + {
1453 + if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1454 + if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1455 + if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1456 + } catch (e) { }
1457 + }
1458 + if (process.platform == 'win32')
1459 + {
1460 + results.pendingReboot = require('win-info').pendingReboot(); // Pending reboot
1461 + if (require('identifiers').volumes != null)
1462 + {
1463 + try
1464 + {
1465 + results.volumes = require('identifiers').volumes();
1466 + }
1467 + catch (e)
1468 + {
1469 + }
1470 }
1418 - } if (this.udpport != null) {
1419 - // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1420 - s.data = onUdpRelayServerTunnelData;
1421 - s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1422 - s.udprelay.bind({ port: 0 });
1423 - s.udprelay.peerindex = this.index;
1424 - s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1425 - s.udprelay.udpport = this.udpport;
1426 - s.udprelay.udpaddr = this.udpaddr;
1427 - s.udprelay.first = true;
1428 -
1429 - // Add the UDP session to the count and update the server
1430 - if (s.httprequest.userid != null) {
1431 - if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1432 - try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1433 - broadcastSessionsToRegisteredApps();
1471 + }
1472 +
1473 + /*
1474 + if (process.platform == 'win32') {
1475 + var defragResult = function (r) {
1476 + if (typeof r == 'object') { results[this.callname] = r; }
1477 + if (this.callname == 'defrag') {
1478 + var pr = require('win-info').installedApps(); // Installed apps
1479 + pr.callname = 'installedApps';
1480 + pr.sessionid = data.sessionid;
1481 + pr.then(defragResult, defragResult);
1482 + }
1483 + else {
1484 + results.winpatches = require('win-info').qfe(); // Windows patches
1485 + results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1486 + func(results);
1487 + }
1488 }
1489 + var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1490 + pr.callname = 'defrag';
1491 + pr.sessionid = data.sessionid;
1492 + pr.then(defragResult, defragResult);
1493 } else {
1436 - // This is a normal connect for KVM/Terminal/Files
1437 - s.data = onTunnelData;
1494 + */
1495 + results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1496 + func(results);
1497 + //}
1498 + } catch (e) { func(null, e); }
1499 +}
1500 +
1501 +// Get a formated response for a given directory path
1502 +function getDirectoryInfo(reqpath)
1503 +{
1504 + var response = { path: reqpath, dir: [] };
1505 + if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32'))
1506 + {
1507 + // List all the drives in the root, or the root itself
1508 + var results = null;
1509 + try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1510 + if (results != null)
1511 + {
1512 + for (var i = 0; i < results.length; ++i)
1513 + {
1514 + var drive = { n: results[i].name, t: 1 };
1515 + if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1516 + response.dir.push(drive);
1517 + }
1518 + }
1519 + } else
1520 + {
1521 + // List all the files and folders in this path
1522 + if (reqpath == '') { reqpath = '/'; }
1523 + var results = null, xpath = obj.path.join(reqpath, '*');
1524 + //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1525 + try { results = fs.readdirSync(xpath); } catch (e) { }
1526 + if (results != null)
1527 + {
1528 + for (var i = 0; i < results.length; ++i)
1529 + {
1530 + if ((results[i] != '.') && (results[i] != '..'))
1531 + {
1532 + var stat = null, p = obj.path.join(reqpath, results[i]);
1533 + //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1534 + try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1535 + if ((stat != null) && (stat != undefined))
1536 + {
1537 + if (stat.isDirectory() == true)
1538 + {
1539 + response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1540 + } else
1541 + {
1542 + response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1543 + }
1544 + }
1545 + }
1546 + }
1547 }
1548 }
1549 + return response;
1550 +}
1551 +
1552 +// Tunnel callback operations
1553 +function onTunnelUpgrade(response, s, head)
1554 +{
1555 + this.s = s;
1556 + s.httprequest = this;
1557 + s.end = onTunnelClosed;
1558 + s.tunnel = this;
1559 + s.descriptorMetadata = "MeshAgent_relayTunnel";
1560
1441 - // Called when UDP relay data is received // TODO****
1442 - function onUdpRelayTargetTunnelConnect(data) {
1443 - var peerTunnel = tunnels[this.peerindex];
1444 - peerTunnel.s.write(data);
1561 + if (require('MeshAgent').idleTimeout != null)
1562 + {
1563 + s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1564 + s.on('timeout', function ()
1565 + {
1566 + this.ping();
1567 + this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1568 + });
1569 }
1570
1447 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1448 - function onUdpRelayServerTunnelData(data) {
1449 - if (this.udprelay.first === true) {
1450 - delete this.udprelay.first; // Skip the first 'c' that is received.
1451 - } else {
1452 - this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1571 + //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1572 +
1573 + if (this.tcpport != null)
1574 + {
1575 + // This is a TCP relay connection, pause now and try to connect to the target.
1576 + s.pause();
1577 + s.data = onTcpRelayServerTunnelData;
1578 + var connectionOptions = { port: parseInt(this.tcpport) };
1579 + if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1580 + s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1581 + s.tcprelay.peerindex = this.index;
1582 +
1583 + // Add the TCP session to the count and update the server
1584 + if (s.httprequest.userid != null)
1585 + {
1586 + if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1587 + try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1588 + broadcastSessionsToRegisteredApps();
1589 + }
1590 + } if (this.udpport != null)
1591 + {
1592 + // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1593 + s.data = onUdpRelayServerTunnelData;
1594 + s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1595 + s.udprelay.bind({ port: 0 });
1596 + s.udprelay.peerindex = this.index;
1597 + s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1598 + s.udprelay.udpport = this.udpport;
1599 + s.udprelay.udpaddr = this.udpaddr;
1600 + s.udprelay.first = true;
1601 +
1602 + // Add the UDP session to the count and update the server
1603 + if (s.httprequest.userid != null)
1604 + {
1605 + if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1606 + try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1607 + broadcastSessionsToRegisteredApps();
1608 }
1609 + } else
1610 + {
1611 + // This is a normal connect for KVM/Terminal/Files
1612 + s.data = onTunnelData;
1613 }
1614 +}
1615 +
1616 +// Called when UDP relay data is received // TODO****
1617 +function onUdpRelayTargetTunnelConnect(data)
1618 +{
1619 + var peerTunnel = tunnels[this.peerindex];
1620 + peerTunnel.s.write(data);
1621 +}
1622
1456 - // Called when the TCP relay target is connected
1457 - function onTcpRelayTargetTunnelConnect() {
1458 - var peerTunnel = tunnels[this.peerindex];
1459 - this.pipe(peerTunnel.s); // Pipe Target --> Server
1460 - peerTunnel.s.first = true;
1461 - peerTunnel.s.resume();
1623 +// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1624 +function onUdpRelayServerTunnelData(data)
1625 +{
1626 + if (this.udprelay.first === true)
1627 + {
1628 + delete this.udprelay.first; // Skip the first 'c' that is received.
1629 + } else
1630 + {
1631 + this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1632 }
1633 +}
1634
1464 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1465 - function onTcpRelayServerTunnelData(data) {
1466 - if (this.first == true) {
1467 - this.first = false;
1468 - this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1469 - }
1635 +// Called when the TCP relay target is connected
1636 +function onTcpRelayTargetTunnelConnect()
1637 +{
1638 + var peerTunnel = tunnels[this.peerindex];
1639 + this.pipe(peerTunnel.s); // Pipe Target --> Server
1640 + peerTunnel.s.first = true;
1641 + peerTunnel.s.resume();
1642 +}
1643 +
1644 +// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1645 +function onTcpRelayServerTunnelData(data)
1646 +{
1647 + if (this.first == true)
1648 + {
1649 + this.first = false;
1650 + this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1651 }
1652 +}
1653
1472 - function onTunnelClosed() {
1473 - var tunnel = tunnels[this.httprequest.index];
1474 - if (tunnel == null) return; // Stop duplicate calls.
1654 +function onTunnelClosed()
1655 +{
1656 + var tunnel = tunnels[this.httprequest.index];
1657 + if (tunnel == null) return; // Stop duplicate calls.
1658
1476 - // If this is a routing session, clean up and send the new session counts.
1477 - if (this.httprequest.userid != null) {
1478 - if (this.httprequest.tcpport != null) {
1479 - 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]; } }
1480 - try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1481 - broadcastSessionsToRegisteredApps();
1482 - } else if (this.httprequest.udpport != null) {
1483 - 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]; } }
1484 - try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1485 - broadcastSessionsToRegisteredApps();
1486 - }
1659 + // If this is a routing session, clean up and send the new session counts.
1660 + if (this.httprequest.userid != null)
1661 + {
1662 + if (this.httprequest.tcpport != null)
1663 + {
1664 + 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]; } }
1665 + try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1666 + broadcastSessionsToRegisteredApps();
1667 + } else if (this.httprequest.udpport != null)
1668 + {
1669 + 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]; } }
1670 + try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1671 + broadcastSessionsToRegisteredApps();
1672 }
1673 + }
1674
1489 - // Sent tunnel statistics to the server, only send this if compression was used.
1490 - if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
1491 - mesh.SendCommand({
1492 - action: 'tunnelCloseStats',
1493 - url: tunnel.url,
1494 - userid: tunnel.userid,
1495 - protocol: tunnel.protocol,
1496 - sessionid: tunnel.sessionid,
1497 - sent: this.bytesSent_uncompressed.toString(),
1498 - sentActual: this.bytesSent_actual.toString(),
1499 - sentRatio: this.bytesSent_ratio,
1500 - received: this.bytesReceived_uncompressed.toString(),
1501 - receivedActual: this.bytesReceived_actual.toString(),
1502 - receivedRatio: this.bytesReceived_ratio
1503 - });
1504 - }
1675 + // Sent tunnel statistics to the server, only send this if compression was used.
1676 + if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString()))
1677 + {
1678 + mesh.SendCommand({
1679 + action: 'tunnelCloseStats',
1680 + url: tunnel.url,
1681 + userid: tunnel.userid,
1682 + protocol: tunnel.protocol,
1683 + sessionid: tunnel.sessionid,
1684 + sent: this.bytesSent_uncompressed.toString(),
1685 + sentActual: this.bytesSent_actual.toString(),
1686 + sentRatio: this.bytesSent_ratio,
1687 + received: this.bytesReceived_uncompressed.toString(),
1688 + receivedActual: this.bytesReceived_actual.toString(),
1689 + receivedRatio: this.bytesReceived_ratio
1690 + });
1691 + }
1692
1506 - //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1507 - if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
1693 + //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1694 + if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
1695
1509 - /*
1510 - // Close the watcher if required
1511 - if (this.httprequest.watcher != undefined) {
1512 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1513 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1514 - delete this.httprequest.watcher;
1515 - }
1516 - */
1696 + /*
1697 + // Close the watcher if required
1698 + if (this.httprequest.watcher != undefined) {
1699 + //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1700 + //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1701 + delete this.httprequest.watcher;
1702 + }
1703 + */
1704 +
1705 + // If there is a upload or download active on this connection, close the file
1706 + if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
1707 + if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1708 +
1709 + // Clean up WebRTC
1710 + if (this.webrtc != null)
1711 + {
1712 + 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; }
1713 + if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1714 + try { this.webrtc.close(); } catch (e) { }
1715 + this.webrtc.removeAllListeners('connected');
1716 + this.webrtc.removeAllListeners('disconnected');
1717 + this.webrtc.removeAllListeners('dataChannel');
1718 + delete this.webrtc;
1719 + }
1720
1518 - // If there is a upload or download active on this connection, close the file
1519 - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
1520 - if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1521 -
1522 - // Clean up WebRTC
1523 - if (this.webrtc != null) {
1524 - 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; }
1525 - if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1526 - try { this.webrtc.close(); } catch (e) { }
1527 - this.webrtc.removeAllListeners('connected');
1528 - this.webrtc.removeAllListeners('disconnected');
1529 - this.webrtc.removeAllListeners('dataChannel');
1530 - delete this.webrtc;
1721 + // Clean up WebSocket
1722 + this.removeAllListeners('data');
1723 +}
1724 +function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1725 +function onTunnelData(data)
1726 +{
1727 + //console.log("OnTunnelData");
1728 + //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1729 +
1730 + // If this is upload data, save it to file
1731 + if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123))
1732 + {
1733 + // Save the data to file being uploaded.
1734 + if (data[0] == 0)
1735 + {
1736 + // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1737 + 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.
1738 + } else
1739 + {
1740 + // If data does not start with zero, save as-is.
1741 + 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.
1742 }
1743 + this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
1744 + return;
1745 + }
1746
1533 - // Clean up WebSocket
1534 - this.removeAllListeners('data');
1747 + if (this.httprequest.state == 0)
1748 + {
1749 + // Check if this is a relay connection
1750 + if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1751 }
1536 - function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1537 - function onTunnelData(data) {
1538 - //console.log("OnTunnelData");
1539 - //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1540 -
1541 - // If this is upload data, save it to file
1542 - if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
1543 - // Save the data to file being uploaded.
1544 - if (data[0] == 0) {
1545 - // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1546 - 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.
1547 - } else {
1548 - // If data does not start with zero, save as-is.
1549 - 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.
1752 + else
1753 + {
1754 + // Handle tunnel data
1755 + if (this.httprequest.protocol == 0)
1756 + { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1757 + // Take a look at the protocol
1758 + if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1759 + this.httprequest.protocol = parseInt(data);
1760 + if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1761 + if (this.httprequest.protocol == 10)
1762 + {
1763 + //
1764 + // Basic file transfer
1765 + //
1766 + var stats = null;
1767 + if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1768 + try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1769 + try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1770 + if (this.httprequest.downloadFile)
1771 + {
1772 + //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1773 + this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1774 + this.httprequest.downloadFile.pipe(this);
1775 + this.httprequest.downloadFile.end = function () { }
1776 + } else
1777 + {
1778 + //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1779 + this.write(JSON.stringify({ op: 'cancel' }));
1780 + }
1781 }
1551 - this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
1552 - return;
1553 - }
1782 + else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1783 + {
1784 + //
1785 + // Remote Terminal
1786 + //
1787
1555 - if (this.httprequest.state == 0) {
1556 - // Check if this is a relay connection
1557 - if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1558 - }
1559 - else {
1560 - // Handle tunnel data
1561 - 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
1562 - // Take a look at the protocol
1563 - if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1564 - this.httprequest.protocol = parseInt(data);
1565 - if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1566 - if (this.httprequest.protocol == 10) {
1567 - //
1568 - // Basic file transfer
1569 - //
1570 - var stats = null;
1571 - if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1572 - try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1573 - try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1574 - if (this.httprequest.downloadFile) {
1575 - //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1576 - this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1577 - this.httprequest.downloadFile.pipe(this);
1578 - this.httprequest.downloadFile.end = function () { }
1579 - } else {
1580 - //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1581 - this.write(JSON.stringify({ op: 'cancel' }));
1582 - }
1788 + // Check user access rights for terminal
1789 + if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0)))
1790 + {
1791 + // Disengage this tunnel, user does not have the rights to do this!!
1792 + this.httprequest.protocol = 999999;
1793 + this.httprequest.s.end();
1794 + sendConsoleText("Error: No Terminal Control Rights.");
1795 + return;
1796 }
1584 - else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) {
1585 - //
1586 - // Remote Terminal
1587 - //
1588 -
1589 - // Check user access rights for terminal
1590 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1591 - // Disengage this tunnel, user does not have the rights to do this!!
1592 - this.httprequest.protocol = 999999;
1593 - this.httprequest.s.end();
1594 - sendConsoleText("Error: No Terminal Control Rights.");
1595 - return;
1596 - }
1797
1598 - this.descriptorMetadata = "Remote Terminal";
1798 + this.descriptorMetadata = "Remote Terminal";
1799
1600 - if (process.platform == 'win32') {
1601 - if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9)) {
1602 - this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1603 - this.httprequest.s.end();
1604 - return;
1605 - }
1800 + if (process.platform == 'win32')
1801 + {
1802 + if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1803 + {
1804 + this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1805 + this.httprequest.s.end();
1806 + return;
1807 }
1808 + }
1809
1608 - var prom = require('promise');
1609 - this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1610 - this.httprequest.tpromise.that = this;
1611 - this.httprequest.tpromise.httprequest = this.httprequest;
1612 -
1613 - this.end = function () {
1614 - if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1615 - if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1810 + var prom = require('promise');
1811 + this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1812 + this.httprequest.tpromise.that = this;
1813 + this.httprequest.tpromise.httprequest = this.httprequest;
1814
1617 - // Remove the terminal session to the count to update the server
1618 - if (this.httprequest.userid != null) {
1619 - 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]; } }
1620 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1621 - broadcastSessionsToRegisteredApps();
1622 - }
1815 + this.end = function ()
1816 + {
1817 + if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1818 + if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1819
1624 - if (process.platform == 'win32') {
1625 - // Unpipe the web socket
1626 - this.unpipe(this.httprequest._term);
1627 - if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1820 + // Remove the terminal session to the count to update the server
1821 + if (this.httprequest.userid != null)
1822 + {
1823 + 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]; } }
1824 + try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1825 + broadcastSessionsToRegisteredApps();
1826 + }
1827
1629 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1630 - if (this.rtcchannel) {
1631 - this.rtcchannel.unpipe(this.httprequest._term);
1632 - if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1633 - }
1828 + if (process.platform == 'win32')
1829 + {
1830 + // Unpipe the web socket
1831 + this.unpipe(this.httprequest._term);
1832 + if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1833
1635 - // Clean up
1636 - if (this.httprequest._term) { this.httprequest._term.end(); }
1637 - this.httprequest._term = null;
1834 + // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1835 + if (this.rtcchannel)
1836 + {
1837 + this.rtcchannel.unpipe(this.httprequest._term);
1838 + if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1839 }
1639 - };
1840
1641 - // Perform User-Consent if needed.
1642 - if (this.httprequest.consent && (this.httprequest.consent & 16)) {
1643 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1644 - var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1645 - if (this.httprequest.soptions != null) {
1646 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1647 - if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1648 - }
1649 - this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1650 - this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1651 - this.httprequest.tpromise._consent.then(
1652 - function () {
1653 - // Success
1654 - MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1655 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1656 - this.retPromise._consent = null;
1657 - this.retPromise._res();
1658 - },
1659 - function (e) {
1660 - // Denied
1661 - MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1662 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1663 - this.retPromise._rej(e.toString());
1664 - });
1841 + // Clean up
1842 + if (this.httprequest._term) { this.httprequest._term.end(); }
1843 + this.httprequest._term = null;
1844 }
1666 - else {
1667 - // User-Consent is not required, so just resolve this promise
1668 - this.httprequest.tpromise._res();
1845 + };
1846 +
1847 + // Perform User-Consent if needed.
1848 + if (this.httprequest.consent && (this.httprequest.consent & 16))
1849 + {
1850 + this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1851 + var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1852 + if (this.httprequest.soptions != null)
1853 + {
1854 + if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1855 + if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1856 }
1857 + this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1858 + this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1859 + this.httprequest.tpromise._consent.then(
1860 + function ()
1861 + {
1862 + // Success
1863 + MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1864 + this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1865 + this.retPromise._consent = null;
1866 + this.retPromise._res();
1867 + },
1868 + function (e)
1869 + {
1870 + // Denied
1871 + MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1872 + this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1873 + this.retPromise._rej(e.toString());
1874 + });
1875 + }
1876 + else
1877 + {
1878 + // User-Consent is not required, so just resolve this promise
1879 + this.httprequest.tpromise._res();
1880 + }
1881 +
1882
1883 + this.httprequest.tpromise.then(
1884 + function ()
1885 + {
1886 + this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1887 + this.httprequest.connectionPromise.ws = this.that;
1888
1672 - this.httprequest.tpromise.then(
1673 - function () {
1674 - this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1675 - this.httprequest.connectionPromise.ws = this.that;
1889 + // Start Terminal
1890 + if (process.platform == 'win32')
1891 + {
1892 + try
1893 + {
1894 + var cols = 80, rows = 25;
1895 + if (this.httprequest.xoptions)
1896 + {
1897 + if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1898 + if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1899 + }
1900
1677 - // Start Terminal
1678 - if (process.platform == 'win32') {
1679 - try {
1680 - var cols = 80, rows = 25;
1681 - if (this.httprequest.xoptions) {
1682 - if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1683 - if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1901 + if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1902 + {
1903 + // Admin Terminal
1904 + if (require('win-virtual-terminal').supported)
1905 + {
1906 + // ConPTY PseudoTerminal
1907 + // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1908 +
1909 + // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1910 + 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] } });
1911 + this.httprequest._dispatcher.httprequest = this.httprequest;
1912 + this.httprequest._dispatcher.on('connection', function (c)
1913 + {
1914 + if (this.httprequest.connectionPromise.completed)
1915 + {
1916 + c.end();
1917 + }
1918 + else
1919 + {
1920 + this.httprequest.connectionPromise._res(c);
1921 + }
1922 + });
1923 }
1685 -
1686 - if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6)) {
1687 - // Admin Terminal
1688 - if (require('win-virtual-terminal').supported) {
1689 - // ConPTY PseudoTerminal
1690 - // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1691 -
1692 - // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1693 - 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] } });
1694 - this.httprequest._dispatcher.httprequest = this.httprequest;
1695 - this.httprequest._dispatcher.on('connection', function (c) {
1696 - if (this.httprequest.connectionPromise.completed) {
1924 + else
1925 + {
1926 + // Legacy Terminal
1927 + this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1928 + }
1929 + }
1930 + else
1931 + {
1932 + // Logged in user
1933 + var userPromise = require('user-sessions').enumerateUsers();
1934 + userPromise.that = this;
1935 + userPromise.then(function (u)
1936 + {
1937 + var that = this.that;
1938 + if (u.Active.length > 0)
1939 + {
1940 + var username = u.Active[0].Username;
1941 + if (require('win-virtual-terminal').supported)
1942 + {
1943 + // ConPTY PseudoTerminal
1944 + 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] } });
1945 + }
1946 + else
1947 + {
1948 + // Legacy Terminal
1949 + 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] } });
1950 + }
1951 + that.httprequest._dispatcher.ws = that;
1952 + that.httprequest._dispatcher.on('connection', function (c)
1953 + {
1954 + if (this.ws.httprequest.connectionPromise.completed)
1955 + {
1956 c.end();
1957 }
1699 - else {
1700 - this.httprequest.connectionPromise._res(c);
1958 + else
1959 + {
1960 + this.ws.httprequest.connectionPromise._res(c);
1961 }
1962 });
1963 }
1704 - else {
1705 - // Legacy Terminal
1706 - this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1707 - }
1708 - }
1709 - else {
1710 - // Logged in user
1711 - var userPromise = require('user-sessions').enumerateUsers();
1712 - userPromise.that = this;
1713 - userPromise.then(function (u) {
1714 - var that = this.that;
1715 - if (u.Active.length > 0) {
1716 - var username = u.Active[0].Username;
1717 - if (require('win-virtual-terminal').supported) {
1718 - // ConPTY PseudoTerminal
1719 - 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] } });
1720 - }
1721 - else {
1722 - // Legacy Terminal
1723 - 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] } });
1724 - }
1725 - that.httprequest._dispatcher.ws = that;
1726 - that.httprequest._dispatcher.on('connection', function (c) {
1727 - if (this.ws.httprequest.connectionPromise.completed) {
1728 - c.end();
1729 - }
1730 - else {
1731 - this.ws.httprequest.connectionPromise._res(c);
1732 - }
1733 - });
1734 - }
1735 - });
1736 - }
1737 - }
1738 - catch (e) {
1739 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1964 + });
1965 }
1966 }
1742 - else {
1743 - try {
1744 - var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1745 - var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1746 - var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1747 -
1748 - var env = { HISTCONTROL: 'ignoreboth' };
1749 - if (this.httprequest.xoptions) {
1750 - if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1751 - if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1752 - }
1753 - var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1754 - if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin) {
1755 - if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1756 - this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1757 - }
1758 - else if (bash) {
1759 - var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1760 - // Spaces at the beginning of lines are needed to hide commands from the command history
1761 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1762 - this.httprequest.connectionPromise._res(p);
1763 - }
1764 - else if (sh) {
1765 - var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1766 - // Spaces at the beginning of lines are needed to hide commands from the command history
1767 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1768 - this.httprequest.connectionPromise._res(p);
1769 - }
1770 - else {
1771 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1772 - }
1967 + catch (e)
1968 + {
1969 + this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1970 + }
1971 + }
1972 + else
1973 + {
1974 + try
1975 + {
1976 + var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1977 + var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1978 + var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1979 +
1980 + var env = { HISTCONTROL: 'ignoreboth' };
1981 + if (this.httprequest.xoptions)
1982 + {
1983 + if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1984 + if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1985 + }
1986 + var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1987 + if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1988 + {
1989 + if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1990 + this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1991 + }
1992 + else if (bash)
1993 + {
1994 + var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1995 + // Spaces at the beginning of lines are needed to hide commands from the command history
1996 + if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1997 + this.httprequest.connectionPromise._res(p);
1998 + }
1999 + else if (sh)
2000 + {
2001 + var p = childProcess.execFile(sh, ['sh'], options); // Start sh
2002 + // Spaces at the beginning of lines are needed to hide commands from the command history
2003 + if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
2004 + this.httprequest.connectionPromise._res(p);
2005 }
1774 - catch (e) {
1775 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
2006 + else
2007 + {
2008 + this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
2009 }
2010 }
2011 + catch (e)
2012 + {
2013 + this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
2014 + }
2015 + }
2016
1779 - this.httprequest.connectionPromise.then(
1780 - function (term) {
1781 - // SUCCESS
1782 - var stdoutstream;
1783 - var stdinstream;
1784 - if (process.platform == 'win32') {
1785 - this.ws.httprequest._term = term;
1786 - this.ws.httprequest._term.tunnel = this.ws;
1787 - stdoutstream = stdinstream = term;
1788 - }
1789 - else {
1790 - term.descriptorMetadata = 'Remote Terminal';
1791 - this.ws.httprequest.process = term;
1792 - this.ws.httprequest.process.tunnel = this.ws;
1793 - term.stderr.stdout = term.stdout;
1794 - term.stderr.on('data', function (c) { this.stdout.write(c); });
1795 - stdoutstream = term.stdout;
1796 - stdinstream = term.stdin;
1797 - this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1798 - term.prependListener('exit', function () { this.tunnel.end(); });
1799 - }
2017 + this.httprequest.connectionPromise.then(
2018 + function (term)
2019 + {
2020 + // SUCCESS
2021 + var stdoutstream;
2022 + var stdinstream;
2023 + if (process.platform == 'win32')
2024 + {
2025 + this.ws.httprequest._term = term;
2026 + this.ws.httprequest._term.tunnel = this.ws;
2027 + stdoutstream = stdinstream = term;
2028 + }
2029 + else
2030 + {
2031 + term.descriptorMetadata = 'Remote Terminal';
2032 + this.ws.httprequest.process = term;
2033 + this.ws.httprequest.process.tunnel = this.ws;
2034 + term.stderr.stdout = term.stdout;
2035 + term.stderr.on('data', function (c) { this.stdout.write(c); });
2036 + stdoutstream = term.stdout;
2037 + stdinstream = term.stdin;
2038 + this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
2039 + term.prependListener('exit', function () { this.tunnel.end(); });
2040 + }
2041
1801 - this.ws.removeAllListeners('data');
1802 - this.ws.on('data', onTunnelControlData);
2042 + this.ws.removeAllListeners('data');
2043 + this.ws.on('data', onTunnelControlData);
2044
1804 - stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1805 - this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2045 + stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2046 + this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2047
1807 - // Add the terminal session to the count to update the server
1808 - if (this.ws.httprequest.userid != null) {
1809 - if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1810 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1811 - broadcastSessionsToRegisteredApps();
1812 - }
2048 + // Add the terminal session to the count to update the server
2049 + if (this.ws.httprequest.userid != null)
2050 + {
2051 + if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
2052 + try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
2053 + broadcastSessionsToRegisteredApps();
2054 + }
2055
1814 - // Toast Notification, if required
1815 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2)) {
1816 - // User Notifications is required
1817 - var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1818 - if (this.ws.httprequest.soptions != null) {
1819 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1820 - 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); }
1821 - }
1822 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2056 + // Toast Notification, if required
2057 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
2058 + {
2059 + // User Notifications is required
2060 + var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
2061 + if (this.ws.httprequest.soptions != null)
2062 + {
2063 + if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2064 + 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); }
2065 }
1824 - },
1825 - function (e) {
1826 - // FAILED to connect terminal
1827 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1828 - this.ws.end();
1829 - });
1830 - },
1831 - function (e) {
1832 - // DO NOT start terminal
1833 - this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1834 - this.that.end();
1835 - });
2066 + try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2067 + }
2068 + },
2069 + function (e)
2070 + {
2071 + // FAILED to connect terminal
2072 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2073 + this.ws.end();
2074 + });
2075 + },
2076 + function (e)
2077 + {
2078 + // DO NOT start terminal
2079 + this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2080 + this.that.end();
2081 + });
2082 + }
2083 + else if (this.httprequest.protocol == 2)
2084 + {
2085 + //
2086 + // Remote KVM
2087 + //
2088 +
2089 + // Check user access rights for desktop
2090 + if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0)))
2091 + {
2092 + // Disengage this tunnel, user does not have the rights to do this!!
2093 + this.httprequest.protocol = 999999;
2094 + this.httprequest.s.end();
2095 + sendConsoleText("Error: No Desktop Control Rights.");
2096 + return;
2097 }
1837 - else if (this.httprequest.protocol == 2) {
1838 - //
1839 - // Remote KVM
1840 - //
1841 -
1842 - // Check user access rights for desktop
1843 - if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1844 - // Disengage this tunnel, user does not have the rights to do this!!
1845 - this.httprequest.protocol = 999999;
1846 - this.httprequest.s.end();
1847 - sendConsoleText("Error: No Desktop Control Rights.");
1848 - return;
1849 - }
2098
1851 - this.descriptorMetadata = "Remote KVM";
2099 + this.descriptorMetadata = "Remote KVM";
2100 +
2101 + // Look for a TSID
2102 + var tsid = null;
2103 + if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
2104 + require('MeshAgent')._tsid = tsid;
2105
1853 - // Look for a TSID
1854 - var tsid = null;
1855 - if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1856 - require('MeshAgent')._tsid = tsid;
2106 + // Remote desktop using native pipes
2107 + this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
2108 + this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
2109 + this.desktop = this.httprequest.desktop;
2110
1858 - // Remote desktop using native pipes
1859 - this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1860 - this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1861 - this.desktop = this.httprequest.desktop;
2111 + // Add ourself to the list of remote desktop sessions
2112 + if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
2113 + this.httprequest.desktop.kvm.tunnels.push(this);
2114
1863 - // Add ourself to the list of remote desktop sessions
1864 - if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1865 - this.httprequest.desktop.kvm.tunnels.push(this);
2115 + // Send a metadata update to all desktop sessions
2116 + var users = {};
2117 + if (this.httprequest.desktop.kvm.tunnels != null)
2118 + {
2119 + 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) { } }
2120 + 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) { } }
2121 + tunnelUserCount.desktop = users;
2122 + try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
2123 + broadcastSessionsToRegisteredApps();
2124 + }
2125 +
2126 + this.end = function ()
2127 + {
2128 + --this.desktop.kvm.connectionCount;
2129 +
2130 + // Remove ourself from the list of remote desktop session
2131 + var i = this.desktop.kvm.tunnels.indexOf(this);
2132 + if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
2133
2134 // Send a metadata update to all desktop sessions
2135 var users = {};
1869 - if (this.httprequest.desktop.kvm.tunnels != null) {
2136 + if (this.httprequest.desktop.kvm.tunnels != null)
2137 + {
2138 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) { } }
2139 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) { } }
2140 tunnelUserCount.desktop = users;
@@ -1874,1258 +2142,1444 @@ function createMeshCore(agent) {
2142 broadcastSessionsToRegisteredApps();
2143 }
2144
1877 - this.end = function () {
1878 - --this.desktop.kvm.connectionCount;
1879 -
1880 - // Remove ourself from the list of remote desktop session
1881 - var i = this.desktop.kvm.tunnels.indexOf(this);
1882 - if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1883 -
1884 - // Send a metadata update to all desktop sessions
1885 - var users = {};
1886 - if (this.httprequest.desktop.kvm.tunnels != null) {
1887 - 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) { } }
1888 - 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) { } }
1889 - tunnelUserCount.desktop = users;
1890 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1891 - broadcastSessionsToRegisteredApps();
1892 - }
2145 + // Unpipe the web socket
2146 + try
2147 + {
2148 + this.unpipe(this.httprequest.desktop.kvm);
2149 + this.httprequest.desktop.kvm.unpipe(this);
2150 + }
2151 + catch (e) { }
2152
1894 - // Unpipe the web socket
1895 - try {
1896 - this.unpipe(this.httprequest.desktop.kvm);
1897 - this.httprequest.desktop.kvm.unpipe(this);
2153 + // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
2154 + if (this.rtcchannel)
2155 + {
2156 + try
2157 + {
2158 + this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
2159 + this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
2160 }
2161 catch (e) { }
2162 + }
2163
1901 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1902 - if (this.rtcchannel) {
1903 - try {
1904 - this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1905 - this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1906 - }
1907 - catch (e) { }
1908 - }
1909 -
1910 - // Place wallpaper back if needed
1911 - // TODO
2164 + // Place wallpaper back if needed
2165 + // TODO
2166
1913 - if (this.desktop.kvm.connectionCount == 0) {
1914 - // Display a toast message. This may not be supported on all platforms.
1915 - // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
2167 + if (this.desktop.kvm.connectionCount == 0)
2168 + {
2169 + // Display a toast message. This may not be supported on all platforms.
2170 + // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
2171
1917 - this.httprequest.desktop.kvm.end();
1918 - if (this.httprequest.desktop.kvm.connectionBar) {
1919 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1920 - this.httprequest.desktop.kvm.connectionBar.close();
1921 - this.httprequest.desktop.kvm.connectionBar = null;
1922 - }
1923 - } else {
1924 - for (var i in this.httprequest.desktop.kvm.users) {
1925 - if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) {
1926 - 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; } }
1927 - this.httprequest.desktop.kvm.users.splice(i, 1);
1928 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1929 - this.httprequest.desktop.kvm.connectionBar.close();
1930 - 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);
1931 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1932 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1933 - MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1934 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1935 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1936 - }
1937 - this.httprequest.desktop.kvm.end();
1938 - });
1939 - break;
1940 - }
2172 + this.httprequest.desktop.kvm.end();
2173 + if (this.httprequest.desktop.kvm.connectionBar)
2174 + {
2175 + this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2176 + this.httprequest.desktop.kvm.connectionBar.close();
2177 + this.httprequest.desktop.kvm.connectionBar = null;
2178 + }
2179 + } else
2180 + {
2181 + for (var i in this.httprequest.desktop.kvm.users)
2182 + {
2183 + if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar)
2184 + {
2185 + 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; } }
2186 + this.httprequest.desktop.kvm.users.splice(i, 1);
2187 + this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2188 + this.httprequest.desktop.kvm.connectionBar.close();
2189 + 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);
2190 + this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
2191 + this.httprequest.desktop.kvm.connectionBar.on('close', function ()
2192 + {
2193 + MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2194 + for (var i in this.httprequest.desktop.kvm._pipedStreams)
2195 + {
2196 + this.httprequest.desktop.kvm._pipedStreams[i].end();
2197 + }
2198 + this.httprequest.desktop.kvm.end();
2199 + });
2200 + break;
2201 }
2202 }
1943 - };
1944 - if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1945 - this.httprequest.desktop.kvm.connectionCount++;
1946 - this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
1947 - this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1948 - this.httprequest.desktop.kvm.rusers.sort();
1949 - this.httprequest.desktop.kvm.users.sort();
1950 - } else {
1951 - this.httprequest.desktop.kvm.connectionCount = 1;
1952 - this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
1953 - this.httprequest.desktop.kvm.users = [this.httprequest.username];
2203 }
2204 + };
2205 + if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount'))
2206 + {
2207 + this.httprequest.desktop.kvm.connectionCount++;
2208 + this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
2209 + this.httprequest.desktop.kvm.users.push(this.httprequest.username);
2210 + this.httprequest.desktop.kvm.rusers.sort();
2211 + this.httprequest.desktop.kvm.users.sort();
2212 + } else
2213 + {
2214 + this.httprequest.desktop.kvm.connectionCount = 1;
2215 + this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
2216 + this.httprequest.desktop.kvm.users = [this.httprequest.username];
2217 + }
2218
1956 - if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1957 - // If we have remote control rights, pipe the KVM input
1958 - this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1959 - } else {
1960 - // We need to only pipe non-mouse & non-keyboard inputs.
1961 - //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1962 - // TODO!!!
1963 - }
2219 + if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)))
2220 + {
2221 + // If we have remote control rights, pipe the KVM input
2222 + this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
2223 + } else
2224 + {
2225 + // We need to only pipe non-mouse & non-keyboard inputs.
2226 + //sendConsoleText('Warning: No Remote Desktop Input Rights.');
2227 + // TODO!!!
2228 + }
2229
1965 - // Perform notification if needed. Toast messages may not be supported on all platforms.
1966 - if (this.httprequest.consent && (this.httprequest.consent & 8)) {
1967 - // User Consent Prompt is required
1968 - // Send a console message back using the console channel, "\n" is supported.
1969 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1970 - var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1971 - if (this.httprequest.soptions != null) {
1972 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1973 - if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1974 - }
1975 - var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1976 - pr.ws = this;
1977 - this.pause();
1978 - this._consentpromise = pr;
1979 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
1980 - pr.then(
1981 - function () {
1982 - // Success
1983 - this.ws._consentpromise = null;
1984 - MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1985 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1986 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1987 - // User Notifications is required
1988 - var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1989 - if (this.ws.httprequest.soptions != null) {
1990 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1991 - 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); }
1992 - }
1993 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2230 + // Perform notification if needed. Toast messages may not be supported on all platforms.
2231 + if (this.httprequest.consent && (this.httprequest.consent & 8))
2232 + {
2233 + // User Consent Prompt is required
2234 + // Send a console message back using the console channel, "\n" is supported.
2235 + this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2236 + var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
2237 + if (this.httprequest.soptions != null)
2238 + {
2239 + if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2240 + if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2241 + }
2242 + var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
2243 + pr.ws = this;
2244 + this.pause();
2245 + this._consentpromise = pr;
2246 + this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2247 + pr.then(
2248 + function ()
2249 + {
2250 + // Success
2251 + this.ws._consentpromise = null;
2252 + MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2253 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
2254 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1))
2255 + {
2256 + // User Notifications is required
2257 + var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2258 + if (this.ws.httprequest.soptions != null)
2259 + {
2260 + if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2261 + 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); }
2262 }
1995 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1996 - // Connection Bar is required
1997 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1998 - this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1999 - this.ws.httprequest.desktop.kvm.connectionBar.close();
2000 - }
2001 - try {
2002 - 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);
2003 - MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2263 + try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2264 + }
2265 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40))
2266 + {
2267 + // Connection Bar is required
2268 + if (this.ws.httprequest.desktop.kvm.connectionBar)
2269 + {
2270 + this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2271 + this.ws.httprequest.desktop.kvm.connectionBar.close();
2272 + }
2273 + try
2274 + {
2275 + 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);
2276 + MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2277 + }
2278 + catch (e)
2279 + {
2280 + if (process.platform != 'darwin')
2281 + {
2282 + MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2283 }
2005 - catch (e) {
2006 - if (process.platform != 'darwin') {
2007 - MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2284 + }
2285 + if (this.ws.httprequest.desktop.kvm.connectionBar)
2286 + {
2287 + this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
2288 + this.ws.httprequest.desktop.kvm.connectionBar.on('close', function ()
2289 + {
2290 + MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2291 + for (var i in this.httprequest.desktop.kvm._pipedStreams)
2292 + {
2293 + this.httprequest.desktop.kvm._pipedStreams[i].end();
2294 }
2009 - }
2010 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
2011 - this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
2012 - this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
2013 - MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2014 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
2015 - this.httprequest.desktop.kvm._pipedStreams[i].end();
2016 - }
2017 - this.httprequest.desktop.kvm.end();
2018 - });
2019 - }
2295 + this.httprequest.desktop.kvm.end();
2296 + });
2297 }
2021 - this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
2022 - this.ws.resume();
2023 - },
2024 - function (e) {
2025 - // User Consent Denied/Failed
2026 - this.ws._consentpromise = null;
2027 - MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2028 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2029 - });
2030 - } else {
2031 - // User Consent Prompt is not required
2032 - if (this.httprequest.consent && (this.httprequest.consent & 1)) {
2033 - // User Notifications is required
2034 - MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2035 - var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2036 - if (this.httprequest.soptions != null) {
2037 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2038 - if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2298 }
2040 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2041 - } else {
2042 - MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2299 + this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
2300 + this.ws.resume();
2301 + },
2302 + function (e)
2303 + {
2304 + // User Consent Denied/Failed
2305 + this.ws._consentpromise = null;
2306 + MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2307 + this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2308 + });
2309 + } else
2310 + {
2311 + // User Consent Prompt is not required
2312 + if (this.httprequest.consent && (this.httprequest.consent & 1))
2313 + {
2314 + // User Notifications is required
2315 + MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2316 + var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2317 + if (this.httprequest.soptions != null)
2318 + {
2319 + if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2320 + if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2321 }
2044 - if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
2045 - // Connection Bar is required
2046 - if (this.httprequest.desktop.kvm.connectionBar) {
2047 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2048 - this.httprequest.desktop.kvm.connectionBar.close();
2049 - }
2050 - try {
2051 - 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);
2052 - MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
2053 - }
2054 - catch (e) {
2055 - MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
2056 - }
2057 - if (this.httprequest.desktop.kvm.connectionBar) {
2058 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
2059 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
2060 - MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2061 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
2062 - this.httprequest.desktop.kvm._pipedStreams[i].end();
2063 - }
2064 - this.httprequest.desktop.kvm.end();
2065 - });
2066 - }
2322 + try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2323 + } else
2324 + {
2325 + MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2326 + }
2327 + if (this.httprequest.consent && (this.httprequest.consent & 0x40))
2328 + {
2329 + // Connection Bar is required
2330 + if (this.httprequest.desktop.kvm.connectionBar)
2331 + {
2332 + this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2333 + this.httprequest.desktop.kvm.connectionBar.close();
2334 + }
2335 + try
2336 + {
2337 + 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);
2338 + MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
2339 + }
2340 + catch (e)
2341 + {
2342 + MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
2343 + }
2344 + if (this.httprequest.desktop.kvm.connectionBar)
2345 + {
2346 + this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
2347 + this.httprequest.desktop.kvm.connectionBar.on('close', function ()
2348 + {
2349 + MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2350 + for (var i in this.httprequest.desktop.kvm._pipedStreams)
2351 + {
2352 + this.httprequest.desktop.kvm._pipedStreams[i].end();
2353 + }
2354 + this.httprequest.desktop.kvm.end();
2355 + });
2356 }
2068 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
2357 }
2358 + this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
2359 + }
2360
2071 - this.removeAllListeners('data');
2072 - this.on('data', onTunnelControlData);
2073 - //this.write('MeshCore KVM Hello!1');
2361 + this.removeAllListeners('data');
2362 + this.on('data', onTunnelControlData);
2363 + //this.write('MeshCore KVM Hello!1');
2364
2075 - } else if (this.httprequest.protocol == 5) {
2076 - //
2077 - // Remote Files
2078 - //
2365 + } else if (this.httprequest.protocol == 5)
2366 + {
2367 + //
2368 + // Remote Files
2369 + //
2370
2080 - // Check user access rights for files
2081 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
2082 - // Disengage this tunnel, user does not have the rights to do this!!
2083 - this.httprequest.protocol = 999999;
2084 - this.httprequest.s.end();
2085 - sendConsoleText("Error: No files control rights.");
2086 - return;
2087 - }
2371 + // Check user access rights for files
2372 + if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0)))
2373 + {
2374 + // Disengage this tunnel, user does not have the rights to do this!!
2375 + this.httprequest.protocol = 999999;
2376 + this.httprequest.s.end();
2377 + sendConsoleText("Error: No files control rights.");
2378 + return;
2379 + }
2380
2089 - this.descriptorMetadata = "Remote Files";
2381 + this.descriptorMetadata = "Remote Files";
2382
2091 - // Add the files session to the count to update the server
2092 - if (this.httprequest.userid != null) {
2093 - if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
2383 + // Add the files session to the count to update the server
2384 + if (this.httprequest.userid != null)
2385 + {
2386 + if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
2387 + try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2388 + broadcastSessionsToRegisteredApps();
2389 + }
2390 +
2391 + this.end = function ()
2392 + {
2393 + // Remove the files session from the count to update the server
2394 + if (this.httprequest.userid != null)
2395 + {
2396 + 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]; } }
2397 try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2398 broadcastSessionsToRegisteredApps();
2399 }
2400 + };
2401
2098 - this.end = function () {
2099 - // Remove the files session from the count to update the server
2100 - if (this.httprequest.userid != null) {
2101 - 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]; } }
2102 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2103 - broadcastSessionsToRegisteredApps();
2104 - }
2105 - };
2106 -
2107 - // Perform notification if needed. Toast messages may not be supported on all platforms.
2108 - if (this.httprequest.consent && (this.httprequest.consent & 32)) {
2109 - // User Consent Prompt is required
2110 - // Send a console message back using the console channel, "\n" is supported.
2111 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2112 - var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2113 - if (this.httprequest.soptions != null) {
2114 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2115 - if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2116 - }
2117 - var pr = require('message-box').create(consentTitle, consentMessage, 30);
2118 - pr.ws = this;
2119 - this.pause();
2120 - this._consentpromise = pr;
2121 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2122 - pr.then(
2123 - function () {
2124 - // Success
2125 - this.ws._consentpromise = null;
2126 - MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2127 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2128 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
2129 - // User Notifications is required
2130 - var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2131 - if (this.ws.httprequest.soptions != null) {
2132 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2133 - 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); }
2134 - }
2135 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2402 + // Perform notification if needed. Toast messages may not be supported on all platforms.
2403 + if (this.httprequest.consent && (this.httprequest.consent & 32))
2404 + {
2405 + // User Consent Prompt is required
2406 + // Send a console message back using the console channel, "\n" is supported.
2407 + this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2408 + var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2409 + if (this.httprequest.soptions != null)
2410 + {
2411 + if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2412 + if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2413 + }
2414 + var pr = require('message-box').create(consentTitle, consentMessage, 30);
2415 + pr.ws = this;
2416 + this.pause();
2417 + this._consentpromise = pr;
2418 + this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2419 + pr.then(
2420 + function ()
2421 + {
2422 + // Success
2423 + this.ws._consentpromise = null;
2424 + MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2425 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2426 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4))
2427 + {
2428 + // User Notifications is required
2429 + var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2430 + if (this.ws.httprequest.soptions != null)
2431 + {
2432 + if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2433 + 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); }
2434 }
2137 - this.ws.resume();
2138 - },
2139 - function (e) {
2140 - // User Consent Denied/Failed
2141 - this.ws._consentpromise = null;
2142 - MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2143 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2144 - });
2145 - } else {
2146 - // User Consent Prompt is not required
2147 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
2148 - // User Notifications is required
2149 - MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2150 - var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2151 - if (this.httprequest.soptions != null) {
2152 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2153 - if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2435 + try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2436 }
2155 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2156 - } else {
2157 - MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2437 + this.ws.resume();
2438 + },
2439 + function (e)
2440 + {
2441 + // User Consent Denied/Failed
2442 + this.ws._consentpromise = null;
2443 + MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2444 + this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2445 + });
2446 + } else
2447 + {
2448 + // User Consent Prompt is not required
2449 + if (this.httprequest.consent && (this.httprequest.consent & 4))
2450 + {
2451 + // User Notifications is required
2452 + MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2453 + var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2454 + if (this.httprequest.soptions != null)
2455 + {
2456 + if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2457 + if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2458 }
2159 - this.resume();
2459 + try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2460 + } else
2461 + {
2462 + MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2463 }
2464 + this.resume();
2465 + }
2466
2162 - // Setup files
2163 - // NOP
2467 + // Setup files
2468 + // NOP
2469 + }
2470 + } else if (this.httprequest.protocol == 1)
2471 + {
2472 + // Send data into terminal stdin
2473 + //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2474 + } else if (this.httprequest.protocol == 2)
2475 + {
2476 + // Send data into remote desktop
2477 + if (this.httprequest.desktop.state == 0)
2478 + {
2479 + this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2480 + this.httprequest.desktop.state = 1;
2481 + } else
2482 + {
2483 + this.httprequest.desktop.write(data);
2484 + }
2485 + } else if (this.httprequest.protocol == 5)
2486 + {
2487 + // Process files commands
2488 + var cmd = null;
2489 + try { cmd = JSON.parse(data); } catch (e) { };
2490 + if (cmd == null) { return; }
2491 + if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2492 + if (cmd.action == undefined) { return; }
2493 + //sendConsoleText('CMD: ' + JSON.stringify(cmd));
2494 +
2495 + if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2496 + //console.log(objToString(cmd, 0, ' '));
2497 + switch (cmd.action)
2498 + {
2499 + case 'ls': {
2500 + /*
2501 + // Close the watcher if required
2502 + var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
2503 + if ((this.httprequest.watcher != undefined) && (samepath == false)) {
2504 + //console.log('Closing watcher: ' + this.httprequest.watcher.path);
2505 + //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2506 + delete this.httprequest.watcher;
2507 + }
2508 + */
2509 +
2510 + // Send the folder content to the browser
2511 + var response = getDirectoryInfo(cmd.path);
2512 + if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2513 + this.write(Buffer.from(JSON.stringify(response)));
2514 +
2515 + /*
2516 + // Start the directory watcher
2517 + if ((cmd.path != '') && (samepath == false)) {
2518 + var watcher = fs.watch(cmd.path, onFileWatcher);
2519 + watcher.tunnel = this.httprequest;
2520 + watcher.path = cmd.path;
2521 + this.httprequest.watcher = watcher;
2522 + //console.log('Starting watcher: ' + this.httprequest.watcher.path);
2523 + }
2524 + */
2525 + break;
2526 }
2165 - } else if (this.httprequest.protocol == 1) {
2166 - // Send data into terminal stdin
2167 - //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2168 - } else if (this.httprequest.protocol == 2) {
2169 - // Send data into remote desktop
2170 - if (this.httprequest.desktop.state == 0) {
2171 - this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2172 - this.httprequest.desktop.state = 1;
2173 - } else {
2174 - this.httprequest.desktop.write(data);
2527 + case 'mkdir': {
2528 + // Create a new empty folder
2529 + fs.mkdirSync(cmd.path);
2530 + MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2531 + break;
2532 }
2176 - } else if (this.httprequest.protocol == 5) {
2177 - // Process files commands
2178 - var cmd = null;
2179 - try { cmd = JSON.parse(data); } catch (e) { };
2180 - if (cmd == null) { return; }
2181 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2182 - if (cmd.action == undefined) { return; }
2183 - //sendConsoleText('CMD: ' + JSON.stringify(cmd));
2184 -
2185 - if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2186 - //console.log(objToString(cmd, 0, ' '));
2187 - switch (cmd.action) {
2188 - case 'ls': {
2189 - /*
2190 - // Close the watcher if required
2191 - var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
2192 - if ((this.httprequest.watcher != undefined) && (samepath == false)) {
2193 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
2194 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2195 - delete this.httprequest.watcher;
2196 - }
2197 - */
2198 -
2199 - // Send the folder content to the browser
2200 - var response = getDirectoryInfo(cmd.path);
2201 - if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2202 - this.write(Buffer.from(JSON.stringify(response)));
2203 -
2204 - /*
2205 - // Start the directory watcher
2206 - if ((cmd.path != '') && (samepath == false)) {
2207 - var watcher = fs.watch(cmd.path, onFileWatcher);
2208 - watcher.tunnel = this.httprequest;
2209 - watcher.path = cmd.path;
2210 - this.httprequest.watcher = watcher;
2211 - //console.log('Starting watcher: ' + this.httprequest.watcher.path);
2533 + case 'rm': {
2534 + // Delete, possibly recursive delete
2535 + for (var i in cmd.delfiles)
2536 + {
2537 + var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2538 + try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2539 + if ((delcount == 1) && !cmd.rec)
2540 + {
2541 + MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2542 + } else
2543 + {
2544 + if (cmd.rec)
2545 + {
2546 + MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2547 + } else
2548 + {
2549 + MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2550 + }
2551 }
2213 - */
2214 - break;
2552 }
2216 - case 'mkdir': {
2217 - // Create a new empty folder
2218 - fs.mkdirSync(cmd.path);
2219 - MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2553 + break;
2554 + }
2555 + case 'markcoredump': {
2556 + // If we are asking for the coredump file, set the right path.
2557 + var coreDumpPath = null;
2558 + if (process.platform == 'win32')
2559 + {
2560 + if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2561 + } else
2562 + {
2563 + if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2564 + }
2565 + if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2566 + break;
2567 + }
2568 + case 'rename':
2569 + {
2570 + // Rename a file or folder
2571 + var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2572 + var newfullpath = obj.path.join(cmd.path, cmd.newname);
2573 + MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2574 + try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2575 break;
2576 }
2222 - case 'rm': {
2223 - // Delete, possibly recursive delete
2224 - for (var i in cmd.delfiles) {
2225 - var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2226 - try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2227 - if ((delcount == 1) && !cmd.rec) {
2228 - MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2229 - } else {
2230 - if (cmd.rec) {
2231 - MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2232 - } else {
2233 - MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2234 - }
2235 - }
2236 - }
2577 + case 'findfile':
2578 + {
2579 + // Search for files
2580 + var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
2581 + if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
2582 + this._search = r;
2583 + r.socket = this;
2584 + r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
2585 + r.socket.path = cmd.path; // Search path
2586 + 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) { } });
2587 + r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
2588 break;
2589 }
2239 - case 'markcoredump': {
2240 - // If we are asking for the coredump file, set the right path.
2241 - var coreDumpPath = null;
2242 - if (process.platform == 'win32') {
2243 - if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2244 - } else {
2245 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2246 - }
2247 - if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2590 + case 'cancelfindfile':
2591 + {
2592 + if (this._search) { this._search.cancel(); this._search = null; }
2593 break;
2594 }
2250 - case 'rename':
2251 - {
2252 - // Rename a file or folder
2253 - var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2254 - var newfullpath = obj.path.join(cmd.path, cmd.newname);
2255 - MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2256 - try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2257 - break;
2258 - }
2259 - case 'findfile':
2260 - {
2261 - // Search for files
2262 - var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
2263 - if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
2264 - this._search = r;
2265 - r.socket = this;
2266 - r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
2267 - r.socket.path = cmd.path; // Search path
2268 - 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) { } });
2269 - r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
2270 - break;
2271 - }
2272 - case 'cancelfindfile':
2273 - {
2274 - if (this._search) { this._search.cancel(); this._search = null; }
2275 - break;
2276 - }
2277 - case 'download':
2278 - {
2279 - // Download a file
2280 - var sendNextBlock = 0;
2281 - if (cmd.sub == 'start') { // Setup the download
2282 - if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
2283 - if (process.platform == 'win32') {
2284 - if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2285 - } else {
2286 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2287 - }
2595 + case 'download':
2596 + {
2597 + // Download a file
2598 + var sendNextBlock = 0;
2599 + if (cmd.sub == 'start')
2600 + { // Setup the download
2601 + if ((cmd.path == null) && (cmd.ask == 'coredump'))
2602 + { // If we are asking for the coredump file, set the right path.
2603 + if (process.platform == 'win32')
2604 + {
2605 + if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2606 + } else
2607 + {
2608 + if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2609 }
2289 - MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2290 - if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2291 - this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2292 - 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; }
2293 - if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2294 - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
2295 - 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; }
2610 }
2297 - // Send the next download block(s)
2298 - while (sendNextBlock > 0) {
2299 - sendNextBlock--;
2300 - var buf = Buffer.alloc(16384);
2301 - var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2302 - this.filedownload.ptr += len;
2303 - if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2304 - this.write(buf.slice(0, len + 4)); // Write as binary
2305 - }
2306 - break;
2611 + MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2612 + if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2613 + this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2614 + 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; }
2615 + if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2616 + } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id))
2617 + { // Download commands
2618 + 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; }
2619 }
2308 - case 'upload':
2620 + // Send the next download block(s)
2621 + while (sendNextBlock > 0)
2622 {
2310 - // Upload a file, browser to agent
2311 - if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2312 - if (cmd.path == undefined) break;
2313 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2314 - this.httprequest.uploadFilePath = filepath;
2315 - MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2316 - try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2317 - this.httprequest.uploadFileid = cmd.reqid;
2318 - if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2319 - break;
2623 + sendNextBlock--;
2624 + var buf = Buffer.alloc(16384);
2625 + var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2626 + this.filedownload.ptr += len;
2627 + if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2628 + this.write(buf.slice(0, len + 4)); // Write as binary
2629 }
2321 - case 'uploaddone':
2630 + break;
2631 + }
2632 + case 'upload':
2633 + {
2634 + // Upload a file, browser to agent
2635 + if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2636 + if (cmd.path == undefined) break;
2637 + var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2638 + this.httprequest.uploadFilePath = filepath;
2639 + MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2640 + try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2641 + this.httprequest.uploadFileid = cmd.reqid;
2642 + if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2643 + break;
2644 + }
2645 + case 'uploaddone':
2646 + {
2647 + // Indicates that an upload is done
2648 + if (this.httprequest.uploadFile)
2649 {
2323 - // Indicates that an upload is done
2324 - if (this.httprequest.uploadFile) {
2325 - fs.closeSync(this.httprequest.uploadFile);
2326 - this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2327 - delete this.httprequest.uploadFile;
2328 - delete this.httprequest.uploadFileid;
2329 - delete this.httprequest.uploadFilePath;
2330 - }
2331 - break;
2650 + fs.closeSync(this.httprequest.uploadFile);
2651 + this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2652 + delete this.httprequest.uploadFile;
2653 + delete this.httprequest.uploadFileid;
2654 + delete this.httprequest.uploadFilePath;
2655 }
2333 - case 'uploadcancel':
2656 + break;
2657 + }
2658 + case 'uploadcancel':
2659 + {
2660 + // Indicates that an upload is canceled
2661 + if (this.httprequest.uploadFile)
2662 {
2335 - // Indicates that an upload is canceled
2336 - if (this.httprequest.uploadFile) {
2337 - fs.closeSync(this.httprequest.uploadFile);
2338 - fs.unlinkSync(this.httprequest.uploadFilePath);
2339 - this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2340 - delete this.httprequest.uploadFile;
2341 - delete this.httprequest.uploadFileid;
2342 - delete this.httprequest.uploadFilePath;
2343 - }
2344 - break;
2663 + fs.closeSync(this.httprequest.uploadFile);
2664 + fs.unlinkSync(this.httprequest.uploadFilePath);
2665 + this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2666 + delete this.httprequest.uploadFile;
2667 + delete this.httprequest.uploadFileid;
2668 + delete this.httprequest.uploadFilePath;
2669 }
2346 - case 'copy':
2670 + break;
2671 + }
2672 + case 'copy':
2673 + {
2674 + // Copy a bunch of files from scpath to dspath
2675 + for (var i in cmd.names)
2676 {
2348 - // Copy a bunch of files from scpath to dspath
2349 - for (var i in cmd.names) {
2350 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2351 - MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2352 - if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2353 - }
2354 - break;
2677 + var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2678 + MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2679 + if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2680 }
2356 - case 'move':
2681 + break;
2682 + }
2683 + case 'move':
2684 + {
2685 + // Move a bunch of files from scpath to dspath
2686 + for (var i in cmd.names)
2687 {
2358 - // Move a bunch of files from scpath to dspath
2359 - for (var i in cmd.names) {
2360 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2361 - MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2362 - if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2363 - }
2364 - break;
2688 + var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2689 + MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2690 + if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2691 }
2366 - case 'zip':
2367 - // Zip a bunch of files
2368 - if (this.zip != null) return; // Zip operating is currently running, exit now.
2369 -
2370 - // Check that the specified files exist & build full paths
2371 - var fp, stat, p = [];
2372 - 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); } }
2373 - if (p.length == 0) return; // No files, quit now.
2374 -
2375 - // Setup file compression
2376 - var ofile = cmd.path + '/' + cmd.output;
2377 - this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2378 - this.zipfile = ofile;
2379 - delete this.zipcancel;
2380 - var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2381 - out.xws = this;
2382 - out.on('close', function () {
2383 - this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2384 - this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2385 - if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
2386 - delete this.xws.zipcancel;
2387 - delete this.xws.zipfile;
2388 - delete this.xws.zip;
2389 - });
2390 - this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
2391 - this.zip.xws = this;
2392 - 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));
2393 - this.zip.pipe(out);
2394 - break;
2395 - case 'cancel':
2396 - // Cancel zip operation if present
2397 - try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
2398 - this.zip = null;
2399 - break;
2400 - default:
2401 - // Unknown action, ignore it.
2402 - break;
2403 - }
2404 - } else if (this.httprequest.protocol == 7) { // Plugin data exchange
2405 - var cmd = null;
2406 - try { cmd = JSON.parse(data); } catch (e) { };
2407 - if (cmd == null) { return; }
2408 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2409 - if (cmd.action == undefined) return;
2410 -
2411 - switch (cmd.action) {
2412 - case 'plugin': {
2413 - try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2692 break;
2693 }
2416 - default: {
2417 - // probably shouldn't happen, but just in case this feature is expanded
2418 - }
2694 + case 'zip':
2695 + // Zip a bunch of files
2696 + if (this.zip != null) return; // Zip operating is currently running, exit now.
2697 +
2698 + // Check that the specified files exist & build full paths
2699 + var fp, stat, p = [];
2700 + 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); } }
2701 + if (p.length == 0) return; // No files, quit now.
2702 +
2703 + // Setup file compression
2704 + var ofile = cmd.path + '/' + cmd.output;
2705 + this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2706 + this.zipfile = ofile;
2707 + delete this.zipcancel;
2708 + var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2709 + out.xws = this;
2710 + out.on('close', function ()
2711 + {
2712 + this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2713 + this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2714 + if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
2715 + delete this.xws.zipcancel;
2716 + delete this.xws.zipfile;
2717 + delete this.xws.zip;
2718 + });
2719 + this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
2720 + this.zip.xws = this;
2721 + 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));
2722 + this.zip.pipe(out);
2723 + break;
2724 + case 'cancel':
2725 + // Cancel zip operation if present
2726 + try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
2727 + this.zip = null;
2728 + break;
2729 + default:
2730 + // Unknown action, ignore it.
2731 + break;
2732 + }
2733 + } else if (this.httprequest.protocol == 7)
2734 + { // Plugin data exchange
2735 + var cmd = null;
2736 + try { cmd = JSON.parse(data); } catch (e) { };
2737 + if (cmd == null) { return; }
2738 + if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2739 + if (cmd.action == undefined) return;
2740 +
2741 + switch (cmd.action)
2742 + {
2743 + case 'plugin': {
2744 + try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2745 + break;
2746 + }
2747 + default: {
2748 + // probably shouldn't happen, but just in case this feature is expanded
2749 }
2420 -
2750 }
2422 - //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2751 +
2752 }
2753 + //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2754 }
2755 +}
2756
2426 - // Delete a directory with a files and directories within it
2427 - function deleteFolderRecursive(path, rec) {
2428 - var count = 0;
2429 - if (fs.existsSync(path)) {
2430 - if (rec == true) {
2431 - fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2432 - var curPath = obj.path.join(path, file);
2433 - if (fs.statSync(curPath).isDirectory()) { // recurse
2434 - count += deleteFolderRecursive(curPath, true);
2435 - } else { // delete file
2436 - fs.unlinkSync(curPath);
2437 - count++;
2438 - }
2439 - });
2440 - }
2441 - fs.unlinkSync(path);
2442 - count++;
2757 +// Delete a directory with a files and directories within it
2758 +function deleteFolderRecursive(path, rec)
2759 +{
2760 + var count = 0;
2761 + if (fs.existsSync(path))
2762 + {
2763 + if (rec == true)
2764 + {
2765 + fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index)
2766 + {
2767 + var curPath = obj.path.join(path, file);
2768 + if (fs.statSync(curPath).isDirectory())
2769 + { // recurse
2770 + count += deleteFolderRecursive(curPath, true);
2771 + } else
2772 + { // delete file
2773 + fs.unlinkSync(curPath);
2774 + count++;
2775 + }
2776 + });
2777 }
2444 - return count;
2445 - };
2778 + fs.unlinkSync(path);
2779 + count++;
2780 + }
2781 + return count;
2782 +}
2783
2447 - // Called when receiving control data on WebRTC
2448 - function onTunnelWebRTCControlData(data) {
2449 - if (typeof data != 'string') return;
2450 - var obj;
2451 - try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2452 - if (obj.type == 'close') {
2453 - //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2454 - try { this.close(); } catch (e) { }
2455 - try { this.xrtc.close(); } catch (e) { }
2456 - }
2784 +// Called when receiving control data on WebRTC
2785 +function onTunnelWebRTCControlData(data)
2786 +{
2787 + if (typeof data != 'string') return;
2788 + var obj;
2789 + try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2790 + if (obj.type == 'close')
2791 + {
2792 + //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2793 + try { this.close(); } catch (e) { }
2794 + try { this.xrtc.close(); } catch (e) { }
2795 }
2796 +}
2797
2459 - // Called when receiving control data on websocket
2460 - function onTunnelControlData(data, ws) {
2461 - var obj;
2462 - if (ws == null) { ws = this; }
2463 - if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2464 - else if (typeof data == 'object') { obj = data; } else { return; }
2465 - //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2466 - //console.log('onTunnelControlData: ' + JSON.stringify(data));
2467 -
2468 - if (obj.action) {
2469 - switch (obj.action) {
2470 - case 'lock': {
2471 - // Lock the current user out of the desktop
2472 - try {
2473 - if (process.platform == 'win32') {
2474 - MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2475 - var child = require('child_process');
2476 - child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2477 - }
2478 - } catch (e) { }
2479 - break;
2480 - }
2481 - default:
2482 - // Unknown action, ignore it.
2483 - break;
2798 +// Called when receiving control data on websocket
2799 +function onTunnelControlData(data, ws)
2800 +{
2801 + var obj;
2802 + if (ws == null) { ws = this; }
2803 + if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2804 + else if (typeof data == 'object') { obj = data; } else { return; }
2805 + //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2806 + //console.log('onTunnelControlData: ' + JSON.stringify(data));
2807 +
2808 + if (obj.action)
2809 + {
2810 + switch (obj.action)
2811 + {
2812 + case 'lock': {
2813 + // Lock the current user out of the desktop
2814 + try
2815 + {
2816 + if (process.platform == 'win32')
2817 + {
2818 + MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2819 + var child = require('child_process');
2820 + child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2821 + }
2822 + } catch (e) { }
2823 + break;
2824 }
2485 - return;
2825 + default:
2826 + // Unknown action, ignore it.
2827 + break;
2828 }
2829 + return;
2830 + }
2831
2488 - switch (obj.type) {
2489 - case 'options': {
2490 - // These are additional connection options passed in the control channel.
2491 - //sendConsoleText('options: ' + JSON.stringify(obj));
2492 - delete obj.type;
2493 - ws.httprequest.xoptions = obj;
2832 + switch (obj.type)
2833 + {
2834 + case 'options': {
2835 + // These are additional connection options passed in the control channel.
2836 + //sendConsoleText('options: ' + JSON.stringify(obj));
2837 + delete obj.type;
2838 + ws.httprequest.xoptions = obj;
2839
2495 - // Set additional user consent options if present
2496 - if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2840 + // Set additional user consent options if present
2841 + if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2842
2498 - break;
2843 + break;
2844 + }
2845 + case 'close': {
2846 + // We received the close on the websocket
2847 + //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2848 + try { ws.close(); } catch (e) { }
2849 + break;
2850 + }
2851 + case 'termsize': {
2852 + // Indicates a change in terminal size
2853 + if (process.platform == 'win32')
2854 + {
2855 + if (ws.httprequest._dispatcher == null) return;
2856 + //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2857 + if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2858 + } else
2859 + {
2860 + if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2861 + //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2862 +
2863 + if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2864 }
2500 - case 'close': {
2501 - // We received the close on the websocket
2502 - //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2503 - try { ws.close(); } catch (e) { }
2504 - break;
2865 + break;
2866 + }
2867 + case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2868 + if (ws.httprequest.protocol == 1)
2869 + { // Terminal
2870 + // 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
2871 + if (process.platform == 'win32')

This file is too large to show in full.