Getting ready for OSX support, added OSX agent installer system.

Ylian Saint-Hilaire committed Sep 7, 2018 at 17:09 UTC 3fa719f1312b8eacb2702e76569902e27e26c124
5 files changed +127 -23
agents/MeshAgentOSXPackager.zip
Binary files /dev/null and b/agents/MeshAgentOSXPackager.zip differ
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.2.0-f",
3 + "version": "0.2.0-h",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/scripts/amt-0.2.0.js
+1 -1
@@ -1,5 +1,5 @@
1 /**
2 -* @fileoverview Intel(r) AMT Communication StackXX
2 +* @fileoverview Intel(r) AMT Communication Stack
3 * @author Ylian Saint-Hilaire
4 * @version v0.2.0b
5 */
views/default.handlebars
+43 -21
@@ -1984,7 +1984,8 @@
1984 function addAgentToMesh(meshid) {
1985 if (xxdialogMode) return;
1986 var mesh = meshes[meshid], x = '';
1987 - x += addHtmlValue('Operating System', '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>Windows</option><option value=1>Linux</option><option value=2>Windows (UnInstall)</option><option value=3>Linux (UnInstall)</option></select>') + '<hr>';
1987 + //x += addHtmlValue('Operating System', '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>Windows</option><option value=1>Linux</option><option value=2>Apple OSX</option><option value=3>Windows (UnInstall)</option><option value=4>Linux (UnInstall)</option></select>') + '<hr>';
1988 + x += addHtmlValue('Operating System', '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>Windows</option><option value=1>Linux</option><option value=3>Windows (UnInstall)</option><option value=4>Linux (UnInstall)</option></select>') + '<hr>';
1989
1990 // Windows agent install
1991 //x += "<div id=agins_windows>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
@@ -1999,6 +2000,11 @@
2000 x += '<textarea id=agins_linux_area rows=2 cols=20 readonly=readonly style=width:100%;resize:none;height:120px;overflow:scroll;font-size:12px readonly></textarea>';
2001 x += "</div>";
2002
2003 + // OSX agent install
2004 + x += "<div id=agins_osx style=display:none>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and install it the computer to manage. This agent installer has server and mesh information embedded within it.<br /><br />";
2005 + x += addHtmlValue('Mesh Agent', '<a href="meshosxagent?id=3&meshid=' + meshid.split('/')[2] + '" target="_blank" title="64bit version of OSX Mesh Agent">OSX Agent (64bit)</a>');
2006 + x += "</div>";
2007 +
2008 // Windows agent uninstall
2009 x += "<div id=agins_windows_un style=display:none>To remove a mesh agent, download the file below, run it and click \"uninstall\".<br /><br />";
2010 x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3" target="_blank" title="32bit version of the MeshAgent">Windows (.exe)</a>');
@@ -2030,8 +2036,9 @@
2036 var v = Q('aginsSelect').value;
2037 QV('agins_windows', v == 0);
2038 QV('agins_linux', v == 1);
2033 - QV('agins_windows_un', v == 2);
2034 - QV('agins_linux_un', v == 3);
2039 + QV('agins_osx', v == 2);
2040 + QV('agins_windows_un', v == 3);
2041 + QV('agins_linux_un', v == 4);
2042 }
2043
2044 function validateDeviceToMesh() {
@@ -5060,35 +5067,49 @@
5067 function p5clearClip() { p5clipboard = null; p5clipboardFolder = null; p5clipboardCut = 0; p5updateClipview(); }
5068
5069 function p5fileDragDrop(e) {
5070 + if (xxdialogMode) return;
5071 haltEvent(e);
5072 QV('bigfail', false);
5073 QV('bigok', false);
5074 //QV('p5fileCatchAllInput', false);
5067 - if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || filetreelocation.length == 0) return;
5068 - var names = [], sizes = [], types = [], datas = [], readercount = e.dataTransfer.files.length;
5069 - for (var i = 0; i < e.dataTransfer.files.length; i++) {
5070 - var reader = new FileReader(), file = e.dataTransfer.files[i];
5071 - names.push(file.name);
5072 - sizes.push(file.size);
5073 - types.push(file.type);
5074 - reader.onload = function (event) {
5075 - console.log(event.target.result.length);
5076 - datas.push(event.target.result);
5077 - if (--readercount == 0) {
5078 - Q('p5fileDragName').value = names.join('*');
5079 - Q('p5fileDragSize').value = sizes.join('*');
5080 - Q('p5fileDragType').value = types.join('*');
5081 - Q('p5fileDragData').value = datas.join('*'); // TODO: This will not work for large files!!! *****************
5082 - Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath);
5083 - Q('p5loginSubmit2').click();
5075 +
5076 + // For Chrome & Firefox
5077 + var error = 0;
5078 + p5uploadFile(); // Display the the dialog box
5079 + try { Q('p5uploadinput').files = e.dataTransfer.files; } catch (ex) { error = 1; } // Set the files in the dialog box
5080 + if (error == 0) { p5uploadFileEx(); } // Press the submit button
5081 + setDialogMode(0); // Close the dialog box
5082 +
5083 + // For IE browser - This will not work with very large files
5084 + if (error == 1) {
5085 + if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || filetreelocation.length == 0) return;
5086 + var names = [], sizes = [], types = [], datas = [], readercount = e.dataTransfer.files.length, totalSize = 0;
5087 + for (var i = 0; i < e.dataTransfer.files.length; i++) { totalSize += e.dataTransfer.files[i].size; }
5088 + if (totalSize > 1300000) { p5uploadFile(); return; } // File is too large, not sure what the real maximum is.
5089 + for (var i = 0; i < e.dataTransfer.files.length; i++) {
5090 + var reader = new FileReader(), file = e.dataTransfer.files[i];
5091 + names.push(file.name);
5092 + sizes.push(file.size);
5093 + types.push(file.type);
5094 + reader.onload = function (event) {
5095 + datas.push(event.target.result);
5096 + if (--readercount == 0) {
5097 + Q('p5fileDragName').value = names.join('*');
5098 + Q('p5fileDragSize').value = sizes.join('*');
5099 + Q('p5fileDragType').value = types.join('*');
5100 + Q('p5fileDragData').value = datas.join('*'); // This will not work for large files, there is a limit on the data size in a field.
5101 + Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath);
5102 + Q('p5loginSubmit2').click();
5103 + }
5104 }
5105 + reader.readAsDataURL(file);
5106 }
5086 - reader.readAsDataURL(file);
5107 }
5108 }
5109
5110 var p5dragtimer = null;
5111 function p5fileDragOver(e) {
5112 + if (xxdialogMode) return;
5113 haltEvent(e);
5114 if (p5dragtimer != null) { clearTimeout(p5dragtimer); p5dragtimer = null; }
5115 var ac = true; // TODO: Set to true if we can accept the file
@@ -5099,6 +5120,7 @@
5120 }
5121
5122 function p5fileDragLeave(e) {
5123 + if (xxdialogMode) return;
5124 haltEvent(e);
5125 if (e.target.id != "p5filetable") {
5126 QV('bigfail', false);
webserver.js
+82
@@ -1630,6 +1630,87 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1630 return obj.certificates.CommonName;
1631 }
1632
1633 + // Create a OSX mesh agent installer
1634 + obj.handleMeshOsxAgentRequest = function (req, res) {
1635 + var domain = checkUserIpAddress(req, res);
1636 + if ((domain == null) || (req.query.id == null)) { res.sendStatus(404); return; }
1637 +
1638 + // If required, check if this user has rights to do this
1639 + if ((obj.parent.config.settings != null) && (obj.parent.config.settings.lockagentdownload == true) && (req.session.userid == null)) { res.sendStatus(401); console.log('2'); return; }
1640 +
1641 + // Send a specific mesh agent back
1642 + var argentInfo = obj.parent.meshAgentBinaries[req.query.id];
1643 + if ((argentInfo == null) || (req.query.meshid == null)) { res.sendStatus(404); console.log('3'); return; }
1644 +
1645 + // We are going to embed the .msh file into the Windows executable (signed or not).
1646 + // First, fetch the mesh object to build the .msh file
1647 + var mesh = obj.meshes['mesh/' + domain.id + '/' + req.query.meshid];
1648 + if (mesh == null) { res.sendStatus(401); return; }
1649 +
1650 + // If required, check if this user has rights to do this
1651 + if ((obj.parent.config.settings != null) && (obj.parent.config.settings.lockagentdownload == true)) {
1652 + var user = obj.users[req.session.userid];
1653 + var escUserId = obj.common.escapeFieldName(user._id);
1654 + if ((user == null) || (mesh.links[escUserId] == null) || ((mesh.links[escUserId].rights & 1) == 0)) { res.sendStatus(401); console.log('4'); return; }
1655 + if (domain.id != mesh.domain) { res.sendStatus(401); console.log('5'); return; }
1656 + }
1657 +
1658 + var meshidhex = new Buffer(req.query.meshid.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
1659 + var serveridhex = new Buffer(obj.agentCertificateHashBase64.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
1660 + var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
1661 +
1662 + // Build the agent connection URL. If we are using a sub-domain or one with a DNS, we need to craft the URL correctly.
1663 + var xdomain = (domain.dns == null) ? domain.id : '';
1664 + if (xdomain != '') xdomain += "/";
1665 + var meshsettings = "MeshName=" + mesh.name + "\r\nMeshType=" + mesh.mtype + "\r\nMeshID=0x" + meshidhex + "\r\nServerID=" + serveridhex + "\r\n";
1666 + if (obj.args.lanonly != true) { meshsettings += "MeshServer=ws" + (obj.args.notls ? '' : 's') + "://" + getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "agent.ashx\r\n"; } else { meshsettings += "MeshServer=local"; }
1667 + if (req.query.tag != null) { meshsettings += "Tag=" + req.query.tag + "\r\n"; }
1668 +
1669 + // Setup the response output
1670 + var archive = require('archiver')('zip', { level: 5 }); // Sets the compression method.
1671 + archive.on('error', function (err) { throw err; });
1672 + res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/zip', 'Content-Disposition': 'attachment; filename=MeshAgent-' + mesh.name + '.zip' });
1673 + archive.pipe(res);
1674 +
1675 + // Opens the "MeshAgentOSXPackager.zip"
1676 + var yauzl = require("yauzl");
1677 + yauzl.open(obj.path.join(__dirname, 'agents', 'MeshAgentOSXPackager.zip'), { lazyEntries: true }, function (err, zipfile) {
1678 + if (err) { res.sendStatus(500); return; }
1679 + zipfile.readEntry();
1680 + zipfile.on("entry", function (entry) {
1681 + if (/\/$/.test(entry.fileName)) {
1682 + // Skip all folder entries
1683 + zipfile.readEntry();
1684 + } else {
1685 + if (entry.fileName == 'Mesh Agent.mpkg/Contents/distribution.dist') {
1686 + // This is a special file entry, we need to fix it.
1687 + zipfile.openReadStream(entry, function (err, readStream) {
1688 + readStream.on("data", function (data) { if (readStream.xxdata) { readStream.xxdata += data; } else { readStream.xxdata = data; } });
1689 + readStream.on("end", function () {
1690 + var welcomemsg = 'Welcome to the MeshCentral agent for OSX\\\n\\\nThis installer will install the mesh agent for "' + mesh.name + '" and allow the administrator to remotely monitor and control this computer over the internet. For more information, go to info.meshcentral.com.\\\n\\\nThis software is provided under Apache 2.0 license.\\\n';
1691 + var installsize = Math.floor((argentInfo.size + meshsettings.length) / 1024);
1692 + archive.append(readStream.xxdata.toString().split('###WELCOMEMSG###').join(welcomemsg).split('###INSTALLSIZE###').join(installsize), { name: entry.fileName });
1693 + zipfile.readEntry();
1694 + });
1695 + });
1696 + } else {
1697 + // Normal file entry
1698 + zipfile.openReadStream(entry, function (err, readStream) {
1699 + if (err) { throw err; }
1700 + archive.append(readStream, { name: entry.fileName });
1701 + readStream.on('end', function () { zipfile.readEntry(); });
1702 + });
1703 + }
1704 + }
1705 + });
1706 + zipfile.on("end", function () {
1707 + archive.file(argentInfo.path, { name: "Mesh Agent.mpkg/Contents/Packages/meshagent.pkg/Contents/MeshAgent" });
1708 + archive.append(meshsettings, { name: "Mesh Agent.mpkg/Contents/Packages/meshagent.pkg/Contents/MeshAgent.msh" });
1709 + archive.finalize();
1710 + });
1711 + });
1712 + }
1713 +
1714 // Handle a request to download a mesh settings
1715 obj.handleMeshSettingsRequest = function (req, res) {
1716 var domain = checkUserIpAddress(req, res);
@@ -1722,6 +1803,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1803 obj.app.ws(url + 'webrelay.ashx', handleRelayWebSocket);
1804 obj.app.ws(url + 'control.ashx', function (ws, req) { try { var domain = checkUserIpAddress(ws, req); if (domain != null) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); } } catch (e) { console.log(e); } });
1805 obj.app.get(url + 'meshagents', obj.handleMeshAgentRequest);
1806 + obj.app.get(url + 'meshosxagent', obj.handleMeshOsxAgentRequest);
1807 obj.app.get(url + 'meshsettings', obj.handleMeshSettingsRequest);
1808 obj.app.get(url + 'downloadfile.ashx', handleDownloadFile);
1809 obj.app.post(url + 'uploadfile.ashx', handleUploadFile);