1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-19 17:53:28 +00:00

Update documentation

This commit is contained in:
Bryan Roe
2022-10-11 00:14:24 -07:00
parent f811d1fc93
commit f5283e30ae
3 changed files with 81 additions and 18 deletions

View File

@@ -14,10 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
//
// Toaster.js provides functionality to be able to pop up a toast notification in a platform agnostic fashion
//
var promise = require('promise'); var promise = require('promise');
if (process.platform == 'linux' || process.platform == 'darwin' || process.platform == 'freebsd') if (process.platform == 'linux' || process.platform == 'darwin' || process.platform == 'freebsd')
{ {
//
// Helper function to find the binary path for the specified application, using the system utility 'whereis'
//
function findPath(app) function findPath(app)
{ {
var child = require('child_process').execFile('/bin/sh', ['sh']); var child = require('child_process').execFile('/bin/sh', ['sh']);
@@ -41,10 +50,15 @@ if (process.platform == 'linux' || process.platform == 'darwin' || process.platf
function Toaster() function Toaster()
{ {
this._ObjectID = 'toaster'; this._ObjectID = 'toaster';
//
// Pops a toast, with the specified titale and caption, using the specified sid
// Returns a promise that resolves when the toast is dismissed
//
this.Toast = function Toast(title, caption, tsid) this.Toast = function Toast(title, caption, tsid)
{ {
var retVal = new promise(function (res, rej) { this._res = res; this._rej = rej; }); var retVal = new promise(function (res, rej) { this._res = res; this._rej = rej; });
if (title == 'MeshCentral') { try { title = require('MeshAgent').displayName; } catch (x) { } } if (title == 'MeshCentral') { try { title = require('MeshAgent').displayName; } catch (x) { } } // IF the tital is the default 'MeshCentral', try to switch it for the agent's displayName
retVal.title = title; retVal.title = title;
retVal.caption = caption; retVal.caption = caption;
@@ -53,8 +67,13 @@ function Toaster()
{ {
case 'win32': case 'win32':
{ {
//
// For Windows, we will use powershell to display the toast. We tried using Shell Notify Icon, but ran into stability issues with it.
// The Powershell interface for it seemed much more reliable
//
var cid; var cid;
retVal.options = { env: { _title: title, _caption: caption } }; retVal.options = { env: { _title: title, _caption: caption } }; // We are putting these values into the environment, becuase Powershell has an issue with passing UTF8 values otherwise
for (var c1e in process.env) for (var c1e in process.env)
{ {
retVal.options.env[c1e] = process.env[c1e]; retVal.options.env[c1e] = process.env[c1e];
@@ -82,26 +101,29 @@ function Toaster()
return (retVal); return (retVal);
} }
// Spawn a powershell process so we can setup everything to display the toast
retVal.child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], retVal.options); retVal.child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], retVal.options);
retVal.child.descriptorMetadata = 'toaster'; retVal.child.descriptorMetadata = 'toaster';
retVal.child.toast = retVal; retVal.child.toast = retVal;
retVal.child.stdout.stdin = retVal.child.stdin; retVal.child.stdout.stdin = retVal.child.stdin;
retVal.child.stderr.stdin = retVal.child.stdin; retVal.child.stderr.stdin = retVal.child.stdin;
retVal.child.stdout.on('data', function (c) { if (c.toString().includes('<DISMISSED>')) { this.stdin.write('exit\n'); } }); retVal.child.stdout.on('data', function (c) { if (c.toString().includes('<DISMISSED>')) { this.stdin.write('exit\n'); } }); // When the toast is dismissed, exit the process
retVal.child.stderr.once('data', function (c) { this.stdin.write('$objBalloon.dispose();exit\n'); }); retVal.child.stderr.once('data', function (c) { this.stdin.write('$objBalloon.dispose();exit\n'); });
retVal.child.stdin.write('[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")\r\n'); retVal.child.stdin.write('[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")\r\n'); // Init
retVal.child.stdin.write('$objBalloon = New-Object System.Windows.Forms.NotifyIcon\r\n'); retVal.child.stdin.write('$objBalloon = New-Object System.Windows.Forms.NotifyIcon\r\n'); // Create the NotifyIcon object
retVal.child.stdin.write('$objBalloon.Icon = [System.Drawing.SystemIcons]::Information\r\n'); retVal.child.stdin.write('$objBalloon.Icon = [System.Drawing.SystemIcons]::Information\r\n'); // Set the icon type
retVal.child.stdin.write('$objBalloon.Visible = $True\r\n'); retVal.child.stdin.write('$objBalloon.Visible = $True\r\n');
retVal.child.stdin.write('Register-ObjectEvent -InputObject $objBalloon -EventName BalloonTipClosed -Action { $objBalloon.dispose();Write-Host "<`DISMISSED`>" }') retVal.child.stdin.write('Register-ObjectEvent -InputObject $objBalloon -EventName BalloonTipClosed -Action { $objBalloon.dispose();Write-Host "<`DISMISSED`>" }') // Set an event handler for when the toast is dismissed
retVal.child.stdin.write('$objBalloon.ShowBalloonTip(10000, $env:_title, $env:_caption, 0)\r\n'); retVal.child.stdin.write('$objBalloon.ShowBalloonTip(10000, $env:_title, $env:_caption, 0)\r\n'); // Show the toast
retVal.child.timeout = setTimeout(function (c) retVal.child.timeout = setTimeout(function (c)
{ {
// Set a timeout to cleanup after 10 seconds. This will cause the process to cleanup if the user doesn't interact with the toast
c.timeout = null; c.timeout = null;
c.stdin.write('$objBalloon.dispose();exit\n'); c.stdin.write('$objBalloon.dispose();exit\n');
}, 10000, retVal.child); }, 10000, retVal.child);
retVal.child.on('exit', function () retVal.child.on('exit', function ()
{ {
// Handler that is called when the powershell process has exited
if (this.timeout != null) { clearTimeout(this.timeout); } if (this.timeout != null) { clearTimeout(this.timeout); }
this.toast._res('DISMISSED'); this.toast._res('DISMISSED');
}); });
@@ -114,6 +136,9 @@ function Toaster()
{ {
try try
{ {
//
// Fetch some necessary configuration information about the currently logged in session, so that we can display the toast in the correct desktop session
//
retVal.consoleUid = require('user-sessions').consoleUid(); retVal.consoleUid = require('user-sessions').consoleUid();
retVal.xinfo = require('monitor-info').getXInfo(retVal.consoleUid); retVal.xinfo = require('monitor-info').getXInfo(retVal.consoleUid);
retVal.username = require('user-sessions').getUsername(retVal.consoleUid); retVal.username = require('user-sessions').getUsername(retVal.consoleUid);
@@ -124,7 +149,7 @@ function Toaster()
return (retVal); return (retVal);
} }
if (require('message-box').zenity) if (require('message-box').zenity) // Check to see if ZENITY is installed on the system
{ {
if (process.platform == 'linux' && !require('linux-dbus').hasService('org.freedesktop.Notifications')) if (process.platform == 'linux' && !require('linux-dbus').hasService('org.freedesktop.Notifications'))
{ {
@@ -144,8 +169,7 @@ function Toaster()
} }
else if (require('message-box').zenity.broken || require('message-box').zenity.version[0] < 3 || (require('message-box').zenity.version[0] == 3 && require('message-box').zenity.version[1] < 10)) else if (require('message-box').zenity.broken || require('message-box').zenity.version[0] < 3 || (require('message-box').zenity.version[0] == 3 && require('message-box').zenity.version[1] < 10))
{ {
// ZENITY Notification is broken for this version
// ZENITY Notification is broken
if (require('message-box').notifysend) if (require('message-box').notifysend)
{ {
// Using notify-send // Using notify-send
@@ -201,6 +225,8 @@ function Toaster()
} }
else else
{ {
// This platform doesn't have ZENITY, so lets check other system utilities we can use
util = findPath('kdialog'); util = findPath('kdialog');
if (util) if (util)
{ {
@@ -240,6 +266,9 @@ function Toaster()
} }
else if (require('message-box').xmessage) else if (require('message-box').xmessage)
{ {
//
// XMESSAGE is very rudimentary, so this is a last resort if nothing else exists.
//
retVal._mb = require('message-box').create(title, caption, 5, 'OK'); retVal._mb = require('message-box').create(title, caption, 5, 'OK');
retVal._mb.ret = retVal; retVal._mb.ret = retVal;
retVal._mb.then(function () { this.ret._res('DISMISSED'); }, function () { this.ret._res('DISMISSED'); }); retVal._mb.then(function () { this.ret._res('DISMISSED'); }, function () { this.ret._res('DISMISSED'); });
@@ -253,6 +282,9 @@ function Toaster()
} }
break; break;
case 'darwin': case 'darwin':
//
// For macOS we implemented this in message-box, so let's use that
//
retVal._toast = require('message-box').notify(title, caption); retVal._toast = require('message-box').notify(title, caption);
retVal._toast.parent = retVal; retVal._toast.parent = retVal;
retVal._toast.then(function (v) { this.parent._res(v); }, function (e) { this.parent._rej(e); }); retVal._toast.then(function (v) { this.parent._res(v); }, function (e) { this.parent._rej(e); });
@@ -263,6 +295,9 @@ function Toaster()
}; };
if(process.platform == 'win32') if(process.platform == 'win32')
{ {
//
// Old way to display toast, using NOTIFY_ShellIcon
//
this._containerToast = function _containerToast(caption, title) this._containerToast = function _containerToast(caption, title)
{ {
var toast; var toast;
@@ -292,6 +327,10 @@ function Toaster()
} }
module.exports = new Toaster(); module.exports = new Toaster();
//
// Helper method on Linux to check if a dbus service exists
//
if (process.platform == 'linux' && !require('linux-dbus').hasService) if (process.platform == 'linux' && !require('linux-dbus').hasService)
{ {
require('linux-dbus').hasService = function hasService(name) require('linux-dbus').hasService = function hasService(name)

View File

@@ -14,10 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
//
// Helper module, to make path processing consistent beween platforms.
//
//
// Creates a path string, using a specified path (path1), filename (path2), and whether to use the Current Working Directory (useCWD)
//
function makePath(path1, path2, useCWD) function makePath(path1, path2, useCWD)
{ {
if (useCWD != null && useCWD) if (useCWD != null && useCWD)
{ {
// If useCWD is specified, path1 is ignored, and it will instead use the current process's working directory
// We're going to take the leaf folder of path1, and place it in the working folder
var tokens = process.cwd().split(process.platform == 'win32' ? '\\' : '/'); var tokens = process.cwd().split(process.platform == 'win32' ? '\\' : '/');
tokens.pop(); tokens.pop();
tokens.push(path1.split(process.platform == 'win32' ? '\\' : '/').pop()); tokens.push(path1.split(process.platform == 'win32' ? '\\' : '/').pop());
@@ -26,11 +36,13 @@ function makePath(path1, path2, useCWD)
if (path2.startsWith('.')) if (path2.startsWith('.'))
{ {
// We're going to substitute .exe with the specified extension in path2
if (path1.toLowerCase().endsWith('.exe')) { path1 = path1.substring(0, path1.length - 4); } if (path1.toLowerCase().endsWith('.exe')) { path1 = path1.substring(0, path1.length - 4); }
path1 += path2; path1 += path2;
} }
else else
{ {
// We're going to take path1, remove the trailing delimiter, and append path2, then convert all the delimiters back to the OS specific delimter
var tokens = path1.split(process.platform == 'win32' ? '\\' : '/'); var tokens = path1.split(process.platform == 'win32' ? '\\' : '/');
tokens.pop(); tokens.pop();
tokens.push(path2); tokens.push(path2);

View File

@@ -15,11 +15,18 @@ limitations under the License.
*/ */
//
// This module provides wget functionality on platforms that lack wget
//
var promise = require('promise'); var promise = require('promise');
var http = require('http'); var http = require('http');
var writable = require('stream').Writable; var writable = require('stream').Writable;
//
// Returns a promise, that connects to a remoteUri, saving the target to localFilePath, using the specified options
//
function wget(remoteUri, localFilePath, wgetoptions) function wget(remoteUri, localFilePath, wgetoptions)
{ {
var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; }); var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
@@ -58,7 +65,9 @@ function wget(remoteUri, localFilePath, wgetoptions)
var reqOptions = require('http').parseUri(remoteUri); var reqOptions = require('http').parseUri(remoteUri);
if (wgetoptions) if (wgetoptions)
{ {
for (var inputOption in wgetoptions) { // Enumerate the specified options, and save them as http options
for (var inputOption in wgetoptions)
{
reqOptions[inputOption] = wgetoptions[inputOption]; reqOptions[inputOption] = wgetoptions[inputOption];
} }
} }
@@ -76,6 +85,9 @@ function wget(remoteUri, localFilePath, wgetoptions)
} }
else else
{ {
//
// The http response was successful, so lets setup the destination stream, and hash stream
//
try try
{ {
this._file = require('fs').createWriteStream(this.promise._localFilePath, { flags: 'wb' }); this._file = require('fs').createWriteStream(this.promise._localFilePath, { flags: 'wb' });
@@ -87,13 +99,13 @@ function wget(remoteUri, localFilePath, wgetoptions)
this.promise._rej(e); this.promise._rej(e);
return; return;
} }
this._sha.on('hash', function (h) { this.promise._res(h.toString('hex')); }); this._sha.on('hash', function (h) { this.promise._res(h.toString('hex')); }); // Resolve the promise with the hash of the received data
this._accumulator = new writable( this._accumulator = new writable(
{ {
write: function(chunk, callback) write: function(chunk, callback)
{ {
this.promise._totalBytes += chunk.length; this.promise._totalBytes += chunk.length;
this.promise.emit('bytes', this.promise._totalBytes); this.promise.emit('bytes', this.promise._totalBytes); // Emit the running total of bytes received as 'bytes'
return (true); return (true);
}, },
final: function(callback) final: function(callback)
@@ -102,9 +114,9 @@ function wget(remoteUri, localFilePath, wgetoptions)
} }
}); });
this._accumulator.promise = this.promise; this._accumulator.promise = this.promise;
imsg.pipe(this._file); imsg.pipe(this._file); // Write the received data to the file stream
imsg.pipe(this._accumulator); imsg.pipe(this._accumulator); // Accumulate the number of bytes received to emit 'byte'
imsg.pipe(this._sha); imsg.pipe(this._sha); // Hash the received data using SHA384
} }
}); });
ret.progress = function () { return (this._totalBytes); }; ret.progress = function () { return (this._totalBytes); };