From f8ec89413a35625c446f99dea35ace501c5b00de Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 29 Aug 2019 16:24:05 -0500 Subject: [PATCH] Refactoring Electron IPC --- package.json | 1 + public/electron.js | 963 ++++-------------------------- src/constants.js | 3 +- src/renderer/ipc/AppIPC.js | 20 + src/renderer/ipc/ConfigIPC.js | 56 ++ src/renderer/ipc/DaemonIPC.js | 47 ++ src/renderer/ipc/DependencyIPC.js | 102 ++++ src/renderer/ipc/DownloadIPC.js | 24 + src/renderer/ipc/FilesystemIPC.js | 31 + src/renderer/ipc/MountsIPC.js | 249 ++++++++ src/renderer/ipc/PlatformIPC.js | 79 +++ src/renderer/ipc/ReleaseIPC.js | 87 +++ src/renderer/ipc/StateIPC.js | 30 + src/renderer/ipc/SystemIPC.js | 16 + src/renderer/ipc/UpgradeIPC.js | 116 ++++ 15 files changed, 988 insertions(+), 836 deletions(-) create mode 100644 src/renderer/ipc/AppIPC.js create mode 100644 src/renderer/ipc/ConfigIPC.js create mode 100644 src/renderer/ipc/DaemonIPC.js create mode 100644 src/renderer/ipc/DependencyIPC.js create mode 100644 src/renderer/ipc/DownloadIPC.js create mode 100644 src/renderer/ipc/FilesystemIPC.js create mode 100644 src/renderer/ipc/MountsIPC.js create mode 100644 src/renderer/ipc/PlatformIPC.js create mode 100644 src/renderer/ipc/ReleaseIPC.js create mode 100644 src/renderer/ipc/StateIPC.js create mode 100644 src/renderer/ipc/SystemIPC.js create mode 100644 src/renderer/ipc/UpgradeIPC.js diff --git a/package.json b/package.json index 0d497b1..eff71cd 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "build/**/*", "node_modules/**/*", "src/helpers.js", + "src/renderer/**/*", "public/detect_linux.sh", "public/install_linux.sh" ], diff --git a/public/electron.js b/public/electron.js index 358b646..a4741d6 100644 --- a/public/electron.js +++ b/public/electron.js @@ -1,44 +1,48 @@ -// Modules to control application life and create native browser window - -const {app, BrowserWindow, Tray, nativeImage, Menu, dialog} = require('electron'); -const {ipcMain} = require('electron'); +const { + app, + BrowserWindow, + dialog, + ipcMain, + Menu, + nativeImage, + Tray +} = require('electron'); +const AutoLaunch = require('auto-launch'); const Constants = require('../src/constants'); +const fs = require('fs'); +const helpers = require('../src/helpers'); +const os = require('os'); const path = require('path'); const url = require('url'); require('electron-debug/index')(); -const os = require('os'); -const helpers = require('../src/helpers'); -const fs = require('fs'); -const unzip = require('unzipper'); -const AutoLaunch = require('auto-launch'); require.extensions['.sh'] = function (module, filename) { module.exports = fs.readFileSync(filename, 'utf8'); }; const detectScript = require('./detect_linux.sh'); -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. -let trayContextMenu; -let mainWindow; -let mainWindowTray; -let mountedData = {}; -let mountedLocations = []; -let expectedUnmount = {}; -let launchHidden = false; -let firstMountCheck = true; -let manualMountDetection = {}; -let platformOverride; +const AppIPC = require('../src/renderer/ipc/AppIPC'); +const ConfigIPC = require('../src/renderer/ipc/ConfigIPC'); +const DaemonIPC = require('../src/renderer/ipc/DaemonIPC'); +const DependencyIPC = require('../src/renderer/ipc/DependencyIPC'); +const DownloadIPC = require('../src/renderer/ipc/DownloadIPC'); +const FilesystemIPC = require('../src/renderer/ipc/FilesystemIPC'); +const MountsIPC = require('../src/renderer/ipc/MountsIPC'); +const PlatformIPC = require('../src/renderer/ipc/PlatformIPC'); +const ReleaseIPC = require('../src/renderer/ipc/ReleaseIPC'); +const StateIPC = require('../src/renderer/ipc/StateIPC'); +const SystemIPC = require('../src/renderer/ipc/SystemIPC'); +const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC'); let isShutdown = false; let isQuiting = false; let isInstalling = false; +let launchHidden = false; +let mainWindow; +let mainWindowTray; +let trayContextMenu; -app.on('before-quit', function () { - isQuiting = true; -}); - -function closeApplication() { +const closeApplication = () => { if (!isShutdown) { isShutdown = true; if (mainWindowTray) { @@ -46,52 +50,25 @@ function closeApplication() { } app.quit(); } -} - -function setWindowVisibility(show) { - if (show) { - mainWindow.show(); - if (os.platform() === 'darwin') { - app.dock.show(); - } - - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - mainWindow.focus(); - } else { - mainWindow.hide(); - if (os.platform() === 'darwin') { - app.dock.hide(); - } - } - - if (trayContextMenu && mainWindowTray) { - trayContextMenu.items[0].checked = show; - mainWindowTray.setContextMenu(trayContextMenu) - } -} - -const unmountAllDrives = () => { - // Reset mount states - for (const provider of Constants.PROVIDER_LIST) { - clearManualMountDetection(provider); - expectedUnmount[provider] = true; - } - - // Unmount all items - for (const i in mountedLocations) { - const data = mountedData[mountedLocations[i]]; - helpers.stopMountProcessSync(data.Version, data.Provider); - } - - mountedLocations = []; - mountedData = {}; }; -function createWindow() { +const configurePrimaryApp = () => { + app.on('second-instance', () => { + if (!isInstalling && mainWindow) { + setWindowVisibility(true); + } + }); + + app.on('ready', createWindow); + + app.on('window-all-closed', () => { + closeApplication(); + }); +}; + +const createWindow = () => { loadUiSettings(); - + let extra = {}; if (os.platform() === 'linux') { extra = { @@ -141,12 +118,12 @@ function createWindow() { // when you should delete the corresponding element. mainWindow = null; - unmountAllDrives(); + MountsIPC.unmountAllDrives(); }); const appPath = (os.platform() === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) : (os.platform() === 'darwin') ? path.resolve(path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')) : - process.env.APPIMAGE; + process.env.APPIMAGE; const autoLauncher = new AutoLaunch({ name: 'Repertory UI', @@ -193,50 +170,21 @@ function createWindow() { mainWindowTray = new Tray(image); autoLauncher - .isEnabled() - .then((enabled) => { - trayContextMenu.items[1].checked = enabled; - mainWindowTray.setToolTip('Repertory UI'); - mainWindowTray.setContextMenu(trayContextMenu) - }) - .catch(() => { - closeApplication(); - }); - - mainWindow.loadURL(startUrl); -} - -let instanceLock = app.requestSingleInstanceLock(); -const configurePrimaryApp = () => { - app.on('second-instance', () => { - if (!isInstalling && mainWindow) { - setWindowVisibility(true); - } - }); - - app.on('ready', createWindow); - - app.on('window-all-closed', () => { + .isEnabled() + .then((enabled) => { + trayContextMenu.items[1].checked = enabled; + mainWindowTray.setToolTip('Repertory UI'); + mainWindowTray.setContextMenu(trayContextMenu) + }) + .catch(() => { closeApplication(); }); -}; -if (!instanceLock) { - setTimeout(() => { - if ((instanceLock = app.requestSingleInstanceLock())) { - configurePrimaryApp(); - } else { - closeApplication(); - } - }, 3000); -} else { - configurePrimaryApp(); -} -const clearManualMountDetection = (provider) => { - if (manualMountDetection[provider]) { - clearInterval(manualMountDetection[provider]); - delete manualMountDetection[provider]; - } + mainWindow.loadURL(startUrl); +}; + +const getMainWindow = () => { + return mainWindow; }; const loadUiSettings = () => { @@ -245,51 +193,61 @@ const loadUiSettings = () => { if (fs.statSync(settingFile).isFile()) { const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8')); launchHidden = settings.launch_hidden; - platformOverride = settings.platform_override; + PlatformIPC.setPlatformOverride(settings.platform_override); } } catch (e) { } }; -const monitorMount = (sender, provider, version, pid, location) => { - manualMountDetection[provider] = setInterval(() => { - helpers - .detectRepertoryMounts(version) - .then(result => { - if (result[provider].PID !== pid) { - if (result[provider].PID === -1) { - clearManualMountDetection(provider); - sender.send(Constants.IPC_Unmount_Drive_Reply, { - data: { - Expected: expectedUnmount[provider], - Location: location, - Provider: provider, - Error: Error(provider + ' Unmounted').toString(), - Success: false, - } - }); - } else { - pid = result[provider].PID; - } - } - }) - .catch(e => { - console.log(e); - }); - },6000); -}; - const saveUiSettings = () => { const settingFile = path.join(helpers.getDataDirectory(), 'ui.json'); try { fs.writeFileSync(settingFile, JSON.stringify({ launch_hidden: launchHidden, - platform_override: platformOverride, + platform_override: PlatformIPC.getPlatformOverride(), }), 'utf-8'); } catch (e) { } }; +const setIsInstalling = installing => { + isInstalling = installing; +}; + +const setTrayImage = driveInUse => { + let image; + if (driveInUse) { + image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_both_mac.png' : '../build/logo_both.png')); + } else { + image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png')); + } + mainWindowTray.setImage(image); +}; + +const setWindowVisibility = show => { + if (show) { + mainWindow.show(); + if (os.platform() === 'darwin') { + app.dock.show(); + } + + if (mainWindow.isMinimized()) { + mainWindow.restore(); + } + mainWindow.focus(); + } else { + mainWindow.hide(); + if (os.platform() === 'darwin') { + app.dock.hide(); + } + } + + if (trayContextMenu && mainWindowTray) { + trayContextMenu.items[0].checked = show; + mainWindowTray.setContextMenu(trayContextMenu) + } +}; + const standardIPCReply = (event, channel, data, error) => { if (mainWindow) { event.sender.send(channel, { @@ -302,699 +260,36 @@ const standardIPCReply = (event, channel, data, error) => { } }; -ipcMain.on(Constants.IPC_Browse_Directory + '_sync', (event, data) => { - dialog.showOpenDialog(mainWindow, { - defaultPath: data.Location, - properties: ['openDirectory'], - title: data.Title, - }, (filePaths) => { - if (filePaths && (filePaths.length > 0)) { - event.returnValue = filePaths[0]; + + + +app.on('before-quit', function () { + isQuiting = true; +}); + +let instanceLock = app.requestSingleInstanceLock(); +if (!instanceLock) { + setTimeout(() => { + if ((instanceLock = app.requestSingleInstanceLock())) { + configurePrimaryApp(); } else { - event.returnValue = ''; + closeApplication(); } - }); -}); + }, 3000); +} else { + configurePrimaryApp(); +} -ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => { - helpers - .checkDaemonVersion(data.Version, data.Provider) - .then(code => { - standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, { - Valid: (code === 0), - Code: code, - }); - }) - .catch(e => { - standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, { - Valid: false, - }, e); - }); -}); -ipcMain.on(Constants.IPC_Check_Daemon_Version + '_sync', (event, data) => { - helpers - .checkDaemonVersion(data.Version, data.Provider) - .then(code => { - event.returnValue = { - data: { - Success: true, - Valid: (code === 0), - Code: code, - }, - }; - }) - .catch(e => { - event.returnValue = { - data: { - Error: e.toString(), - Success: false, - Valid: false - }, - }; - }); -}); - -ipcMain.on(Constants.IPC_Check_Dependency_Installed, (event, data) => { - try { - const exists = fs.lstatSync(data.File).isFile(); - standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, { - data: { - Exists: exists, - }, - }); - } catch (e) { - standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, { - data : { - Exists: false, - }, - }); - } -}); - -ipcMain.on(Constants.IPC_Check_Dependency_Installed + '_sync', (event, data) => { - try { - const ls = fs.lstatSync(data.File); - event.returnValue = { - data: { - Exists: ls.isFile() || ls.isSymbolicLink(), - }, - }; - } catch (e) { - event.returnValue = { - data: { - Exists: false - }, - }; - } -}); - -ipcMain.on(Constants.IPC_Check_Installed, (event, data) => { - const destination = path.join(helpers.getDataDirectory(), data.Version); - helpers - .getMissingDependencies(data.Dependencies) - .then((dependencies) => { - let exists = false; - try { - exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory(); - } catch (e) { - } - standardIPCReply(event, Constants.IPC_Check_Installed_Reply, { - Dependencies: dependencies, - Exists: exists, - Version: data.Version, - }); - }).catch(error => { - standardIPCReply(event, Constants.IPC_Check_Installed_Reply, { - Dependencies: [], - Version: data.Version, - }, error); - }); -}); - -ipcMain.on(Constants.IPC_Check_Mount_Location + '_sync', (event, data) => { - let response = { - Success: true, - Error: '' - }; - - try { - if (fs.existsSync(data.Location) && fs.statSync(data.Location).isDirectory()) { - if (fs.readdirSync(data.Location).length !== 0) { - response.Success = false; - response.Error = 'Directory not empty: ' + data.Location; - } - } else { - response.Success = false; - response.Error = 'Directory not found: ' + data.Location; - } - } catch (e) { - response.Success = false; - response.Error = e.toString(); - } - event.returnValue = response; -}); - -ipcMain.on(Constants.IPC_Delete_File, (event, data) => { - try { - if (fs.existsSync(data.FilePath)) { - fs.unlinkSync(data.FilePath); - } - } catch (e) { - } -}); - -ipcMain.on(Constants.IPC_Delete_File + '_sync', (event, data) => { - try { - if (fs.existsSync(data.FilePath)) { - fs.unlinkSync(data.FilePath); - } - event.returnValue = { - data: true, - }; - } catch (e) { - event.returnValue = { - data: false, - }; - } -}); - -ipcMain.on(Constants.IPC_Detect_Mounts, (event, data) => { - let driveLetters = {}; - for (const provider of Constants.PROVIDER_LIST) { - driveLetters[provider] = []; - } - - const grabDriveLetters = (locations) => { - for (let i = 'c'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) { - const drive = (String.fromCharCode(i) + ':').toUpperCase(); - let driveInUse; - if (Object.keys(locations).length > 0) { - for (const provider of Constants.PROVIDER_LIST) { - driveInUse = locations[provider].startsWith(drive); - if (driveInUse) - break; - } - } - if (!driveInUse) { - try { - if (!fs.existsSync(drive)) { - for (const provider of Constants.PROVIDER_LIST) { - driveLetters[provider].push(drive); - } - } - } catch (e) { - } - } - } - - if (Object.keys(locations).length > 0) { - for (const provider of Constants.PROVIDER_LIST) { - if (locations[provider].length > 0) { - if (!driveLetters[provider].find((driveLetter) => { - return driveLetter === locations[provider]; - })) { - driveLetters[provider].push(locations[provider]); - } - } - } - } - }; - - const setImage = (locations) => { - let driveInUse; - if (Object.keys(locations).length > 0) { - for (const provider of Constants.PROVIDER_LIST) { - driveInUse = locations[provider].length > 0; - if (driveInUse) - break; - } - } - - let image; - if (driveInUse) { - image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_both_mac.png' : '../build/logo_both.png')); - } else { - image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png')); - } - - mainWindowTray.setImage(image); - }; - - helpers - .detectRepertoryMounts(data.Version) - .then((results) => { - let storageData = {}; - let locations = {}; - for (const provider of Constants.PROVIDER_LIST) { - storageData[provider] = results[provider] ? results[provider] : { - Active: false, - Location: '', - PID: -1, - }; - locations[provider] = storageData[provider].Location; - - if (storageData[provider].PID !== -1) { - expectedUnmount[provider] = false; - if (firstMountCheck) { - monitorMount(event.sender, provider, data.Version, storageData[provider].PID, storageData[provider].Location); - } - } - } - - if (os.platform() === 'win32') { - grabDriveLetters(locations); - } - - setImage(locations); - if (firstMountCheck) { - firstMountCheck = false; - } - standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, { - DriveLetters: driveLetters, - Locations: locations, - }); - }) - .catch(error => { - if (os.platform() === 'win32') { - grabDriveLetters({}); - } - setImage({}); - standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, { - DriveLetters: driveLetters, - }, error); - }); -}); - -ipcMain.on(Constants.IPC_Download_File, (event, data) => { - const destination = path.join(helpers.getDataDirectory(), data.Filename); - helpers.downloadFile(data.URL, destination, (progress) => { - standardIPCReply(event, Constants.IPC_Download_File_Progress, { - Destination: destination, - Progress: progress, - URL: data.URL, - }); - }, error => { - standardIPCReply(event, Constants.IPC_Download_File_Complete, { - Destination: destination, - URL: data.URL, - }, error); - }); -}); - -ipcMain.on(Constants.IPC_Extract_Release, (event, data) => { - const destination = path.join(helpers.getDataDirectory(), data.Version); - helpers.removeDirectoryRecursively(destination); - helpers.mkDirByPathSync(destination); - - const stream = fs.createReadStream(data.Source); - stream - .pipe(unzip.Extract({ path: destination })) - .on('error', error => { - try { - helpers.removeDirectoryRecursively(destination); - } catch (e) { - } - stream.close(); - standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { - Source: data.Source, - }, error); - }) - .on('finish', () => { - stream.close(); - if (os.platform() !== 'win32') { - helpers - .executeAndWait("chmod +x \"" + path.join(destination, 'repertory') + "\"") - .then(() => { - standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { - Source: data.Source, - }); - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { - Source: data.Source, - }, error); - }) - } else { - standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { - Source: data.Source, - }); - } - }); -}); - -ipcMain.on(Constants.IPC_Get_Config, (event, data) => { - helpers - .getConfig(data.Version, data.Provider) - .then((data) => { - if (data.Code === 0) { - standardIPCReply(event, Constants.IPC_Get_Config_Reply, { - Config: data.Data, - }); - } else { - standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, data.Code); - } - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error); - }); -}); - -ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => { - helpers - .getConfigTemplate(data.Version, data.Provider) - .then((data) => { - standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, { - Template: data, - }); - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error); - }); -}); - -ipcMain.on(Constants.IPC_Get_Platform, (event) => { - const sendResponse = (appPlatform, platform) => { - event.sender.send(Constants.IPC_Get_Platform_Reply, { - AppPlatform: appPlatform, - Platform: platform, - }); - }; - - const platform = os.platform(); - if (platform === 'linux') { - if (platformOverride && (platformOverride.trim().length > 0)) { - sendResponse(platformOverride.trim(), 'linux'); - } else { - const scriptFile = path.join(os.tmpdir(), 'repertory_detect_linux.sh'); - fs.writeFileSync(scriptFile, detectScript); - - helpers - .executeScript(scriptFile) - .then(data => { - let appPlatform = data.replace(/(\r\n|\n|\r)/gm, ""); - if (appPlatform === 'unknown') { - helpers - .downloadFile(Constants.LINUX_DETECT_SCRIPT_URL, scriptFile, null, err => { - if (err) { - sendResponse(appPlatform, platform); - } else { - helpers - .executeScript(scriptFile) - .then(data => { - appPlatform = data.replace(/(\r\n|\n|\r)/gm, ""); - sendResponse(appPlatform, platform); - }) - .catch(() => { - sendResponse(appPlatform, platform); - }); - } - }); - } else { - sendResponse(appPlatform, platform); - } - }) - .catch(() => { - sendResponse(platform, platform); - }); - } - } else { - sendResponse(platform, platform); - } -}); - -ipcMain.on(Constants.IPC_Get_State, event => { - helpers.mkDirByPathSync(helpers.getDataDirectory()); - const configFile = path.join(helpers.getDataDirectory(), 'settings.json'); - if (fs.existsSync(configFile)) { - event.sender.send(Constants.IPC_Get_State_Reply, { - data: JSON.parse(fs.readFileSync(configFile, 'utf8')), - }); - } else { - event.sender.send(Constants.IPC_Get_State_Reply, { - data: null, - }); - } -}); - -ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => { - if (data.Source.toLowerCase().endsWith('.dmg')) { - helpers - .executeAsync('open', ['-a', 'Finder', '-W', data.Source]) - .then(() => { - standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { - Source: data.Source, - URL: data.URL, - }); - }) - .catch(error=> { - standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { - Source: data.Source, - URL: data.URL, - }, error); - }); - } else { - const execInstall = () => { - helpers - .executeAndWait(data.Source) - .then(() => { - standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { - Source: data.Source, - URL: data.URL, - }); - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { - Source: data.Source, - URL: data.URL, - }, error); - }); - }; - if (data.IsWinFSP) { - helpers - .performWindowsUninstall(["WinFsp 2019.1", "WinFsp 2019.2", "WinFsp 2019.3 B1", "WinFsp 2019.3 B2"]) - .then(uninstalled => { - if (uninstalled) { - standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { - RebootRequired: true, - Source: data.Source, - URL: data.URL, - }); - } else { - execInstall(); - } - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { - Source: data.Source, - URL: data.URL, - }, error); - }); - } else { - execInstall(); - } - } -}); - -ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => { - let allowSkipVerification = true; - - unmountAllDrives(); - - let tempSig; - let tempPub; - const cleanupFiles = () => { - try { - if (tempSig) { - fs.unlinkSync(tempSig); - } - if (tempPub) { - fs.unlinkSync(tempPub); - } - } catch (e) { - } - }; - - const errorHandler = err => { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { - AllowSkipVerification: allowSkipVerification, - Source: data.Source, - }, err); - }; - - // TODO Enable verification in 1.0.4 - const hasSignature = false;//!data.SkipVerification && data.Signature && (data.Signature.length > 0); - const hasHash = false;//!data.SkipVerification && data.Sha256 && (data.Sha256.length > 0); - if (hasSignature) { - try { - const files = helpers.createSignatureFiles(data.Signature, Constants.DEV_PUBLIC_KEY); - tempPub = files.PublicKeyFile; - tempSig = files.SignatureFile; - } catch (e) { - errorHandler(e); - return; - } - } - - let command; - let args; - const platform = os.platform(); - if (platform === 'win32') { - command = data.Source; - } else if (platform === 'darwin') { - command = 'open'; - args = ['-a', 'Finder', data.Source]; - } else if (platform === 'linux') { - try { - command = data.Source; - fs.chmodSync(command, '750'); - } catch (e) { - errorHandler(e); - } - } else { - errorHandler(Error('Platform not supported: ' + os.platform())); - } - - if (command) { - const executeInstall = () => { - isInstalling = true; - helpers - .executeAsync(command, args) - .then(() => { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply) - }) - .catch(error => { - isInstalling = false; - errorHandler(error); - }); - }; - - if (hasSignature) { - helpers - .verifySignature(data.Source, tempSig, tempPub) - .then(() => { - executeInstall(); - }) - .catch(() => { - errorHandler(Error('Failed to verify installation package signature')); - }); - } else if (hasHash) { - helpers - .verifyHash(data.Source, data.Sha256) - .then(()=> { - executeInstall(); - }) - .catch(() => { - errorHandler(Error('Failed to verify installation package hash')); - }); - } else { - if (platform === 'darwin') { - setTimeout(executeInstall, 3000); - } else { - executeInstall(); - } - } - } else { - errorHandler(Error('Unsupported upgrade: ' + data.Source)); - } -}); - -ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => { - expectedUnmount[data.Provider] = false; - - if (mountedLocations.indexOf(data.Location) !== -1) { - console.log(data.Provider + ' already mounted: ' + data.Location); - } else { - mountedLocations.push(data.Location); - mountedData[data.Location] = { - Version: data.Version, - Provider: data.Provider, - }; - const errorHandler = (pid, error) => { - if (mountedLocations.indexOf(data.Location) !== -1) { - mountedLocations.splice(mountedLocations.indexOf(data.Location), 1); - delete mountedData[data.Location]; - } - - standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, { - Expected: expectedUnmount[data.Provider], - Location: data.Location, - Provider: data.Provider, - }, error || Error(data.Provider + ' Unmounted')); - }; - helpers - .executeMount(data.Version, data.Provider, data.Location, data.NoConsoleSupported, (error, pid) => { - errorHandler(pid, error); - }) - .then(() => { - standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, { - Provider: data.Provider, - }); - }) - .catch(error => { - errorHandler(-1, error); - }); - } -}); - -ipcMain.on(Constants.IPC_Reboot_System, () => { - if (os.platform() === 'win32') { - helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']); - } - closeApplication(); -}); - -ipcMain.on(Constants.IPC_Save_State, (event, data) => { - helpers.mkDirByPathSync(helpers.getDataDirectory()); - const configFile = path.join(helpers.getDataDirectory(), 'settings.json'); - fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8'); -}); - -ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => { - const setConfigValue = (i) => { - if (i < data.Items.length) { - helpers - .setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Version) - .then(() => { - setConfigValue(++i); - }) - .catch(() => { - setConfigValue(++i); - }); - } else { - standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}); - } - }; - setConfigValue(0); -}); - -ipcMain.on(Constants.IPC_Set_Linux_AppPlatform, (event, data) => { - platformOverride = data.AppPlatform; - saveUiSettings(); - event.returnValue = true; -}); - -ipcMain.on(Constants.IPC_Shutdown, () => { - closeApplication(); -}); - -ipcMain.on(Constants.IPC_Show_Window, () => { - setWindowVisibility(true); -}); - -ipcMain.on(Constants.IPC_Show_Window + '_sync', event => { - setWindowVisibility(true); - event.returnValue = true; -}); - -ipcMain.on(Constants.IPC_Test_Release, (event, data) => { - helpers - .testRepertoryBinary(data.Version) - .then(() => { - standardIPCReply(event, Constants.IPC_Test_Release_Reply, {}); - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Test_Release_Reply, {}, error); - }); -}); - -ipcMain.on(Constants.IPC_Unmount_All_Drives, (event, data) => { - unmountAllDrives(); - standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply); -}); - -ipcMain.on(Constants.IPC_Unmount_Drive, (event, data) => { - clearManualMountDetection(data.Provider); - - expectedUnmount[data.Provider] = true; - helpers - .stopMountProcess(data.Version, data.Provider) - .then(result => { - console.log(result); - }) - .catch(e => { - console.log(e); - }); -}); +AppIPC.addListeners(ipcMain, closeApplication, setWindowVisibility); +ConfigIPC.addListeners(ipcMain, standardIPCReply); +DaemonIPC.addListeners(ipcMain, standardIPCReply); +DependencyIPC.addListeners(ipcMain, standardIPCReply); +DownloadIPC.addListeners(ipcMain, standardIPCReply); +FilesystemIPC.addListeners(ipcMain, getMainWindow, dialog); +MountsIPC.addListeners(ipcMain, setTrayImage, standardIPCReply); +PlatformIPC.addListeners(ipcMain, detectScript, saveUiSettings); +ReleaseIPC.addListeners(ipcMain, standardIPCReply); +StateIPC.addListeners(ipcMain); +SystemIPC.addListeners(ipcMain, closeApplication); +UpgradeIPC.addListeners(ipcMain, setIsInstalling, MountsIPC.unmountAllDrives, standardIPCReply); \ No newline at end of file diff --git a/src/constants.js b/src/constants.js index ddb1916..a74beef 100644 --- a/src/constants.js +++ b/src/constants.js @@ -28,8 +28,7 @@ exports.DEV_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n' + '-----END PUBLIC KEY-----'; -//const REPERTORY_BRANCH = '1.1.0-release_branch'; -const REPERTORY_BRANCH = 'master'; +const REPERTORY_BRANCH = '1.1.0-release_branch'; const REPERTORY_UI_BRANCH = '1.0.8_branch'; exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/releases.json'; diff --git a/src/renderer/ipc/AppIPC.js b/src/renderer/ipc/AppIPC.js new file mode 100644 index 0000000..4fdd3da --- /dev/null +++ b/src/renderer/ipc/AppIPC.js @@ -0,0 +1,20 @@ +const Constants = require('../../constants'); + +const addListeners = (ipcMain, closeApplication, setWindowVisibility) => { + ipcMain.on(Constants.IPC_Shutdown, () => { + closeApplication(); + }); + + ipcMain.on(Constants.IPC_Show_Window, () => { + setWindowVisibility(true); + }); + + ipcMain.on(Constants.IPC_Show_Window + '_sync', event => { + setWindowVisibility(true); + event.returnValue = true; + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/ConfigIPC.js b/src/renderer/ipc/ConfigIPC.js new file mode 100644 index 0000000..6f15ba0 --- /dev/null +++ b/src/renderer/ipc/ConfigIPC.js @@ -0,0 +1,56 @@ +const Constants = require('../../constants'); +const helpers = require('../../helpers'); + +const addListeners = (ipcMain, standardIPCReply) => { + ipcMain.on(Constants.IPC_Get_Config, (event, data) => { + helpers + .getConfig(data.Version, data.Provider) + .then((data) => { + if (data.Code === 0) { + standardIPCReply(event, Constants.IPC_Get_Config_Reply, { + Config: data.Data, + }); + } else { + standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, data.Code); + } + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error); + }); + }); + + ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => { + helpers + .getConfigTemplate(data.Version, data.Provider) + .then((data) => { + standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, { + Template: data, + }); + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error); + }); + }); + + ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => { + const setConfigValue = (i) => { + if (i < data.Items.length) { + helpers + .setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Version) + .then(() => { + setConfigValue(++i); + }) + .catch(() => { + setConfigValue(++i); + }); + } else { + standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}); + } + }; + setConfigValue(0); + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/DaemonIPC.js b/src/renderer/ipc/DaemonIPC.js new file mode 100644 index 0000000..c563c1c --- /dev/null +++ b/src/renderer/ipc/DaemonIPC.js @@ -0,0 +1,47 @@ +const Constants = require('../../constants'); +const helpers = require('../../helpers'); + +const addListeners = (ipcMain, standardIPCReply) => { + ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => { + helpers + .checkDaemonVersion(data.Version, data.Provider) + .then(code => { + standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, { + Valid: (code === 0), + Code: code, + }); + }) + .catch(e => { + standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, { + Valid: false, + }, e); + }); + }); + + ipcMain.on(Constants.IPC_Check_Daemon_Version + '_sync', (event, data) => { + helpers + .checkDaemonVersion(data.Version, data.Provider) + .then(code => { + event.returnValue = { + data: { + Success: true, + Valid: (code === 0), + Code: code, + }, + }; + }) + .catch(e => { + event.returnValue = { + data: { + Error: e.toString(), + Success: false, + Valid: false + }, + }; + }); + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/DependencyIPC.js b/src/renderer/ipc/DependencyIPC.js new file mode 100644 index 0000000..6e884f7 --- /dev/null +++ b/src/renderer/ipc/DependencyIPC.js @@ -0,0 +1,102 @@ +const Constants = require('../../constants'); +const fs = require('fs'); +const helpers = require('../../helpers'); + +const addListeners = (ipcMain, standardIPCReply) => { + ipcMain.on(Constants.IPC_Check_Dependency_Installed, (event, data) => { + try { + const exists = fs.lstatSync(data.File).isFile(); + standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, { + data: { + Exists: exists, + }, + }); + } catch (e) { + standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, { + data : { + Exists: false, + }, + }); + } + }); + + ipcMain.on(Constants.IPC_Check_Dependency_Installed + '_sync', (event, data) => { + try { + const ls = fs.lstatSync(data.File); + event.returnValue = { + data: { + Exists: ls.isFile() || ls.isSymbolicLink(), + }, + }; + } catch (e) { + event.returnValue = { + data: { + Exists: false + }, + }; + } + }); + + ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => { + if (data.Source.toLowerCase().endsWith('.dmg')) { + helpers + .executeAsync('open', ['-a', 'Finder', '-W', data.Source]) + .then(() => { + standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { + Source: data.Source, + URL: data.URL, + }); + }) + .catch(error=> { + standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { + Source: data.Source, + URL: data.URL, + }, error); + }); + } else { + const execInstall = () => { + helpers + .executeAndWait(data.Source) + .then(() => { + standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { + Source: data.Source, + URL: data.URL, + }); + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { + Source: data.Source, + URL: data.URL, + }, error); + }); + }; + if (data.IsWinFSP) { + helpers + .performWindowsUninstall(["WinFsp 2019.1", "WinFsp 2019.2", "WinFsp 2019.3 B1", "WinFsp 2019.3 B2"]) + .then(uninstalled => { + if (uninstalled) { + standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { + RebootRequired: true, + Source: data.Source, + URL: data.URL, + }); + } else { + execInstall(); + } + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { + Source: data.Source, + URL: data.URL, + }, error); + }); + } else { + execInstall(); + } + } + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/DownloadIPC.js b/src/renderer/ipc/DownloadIPC.js new file mode 100644 index 0000000..30b407e --- /dev/null +++ b/src/renderer/ipc/DownloadIPC.js @@ -0,0 +1,24 @@ +const Constants = require('../../constants'); +const helpers = require('../../helpers'); + +const addListeners = (ipcMain, standardIPCReply) => { + ipcMain.on(Constants.IPC_Download_File, (event, data) => { + const destination = path.join(helpers.getDataDirectory(), data.Filename); + helpers.downloadFile(data.URL, destination, (progress) => { + standardIPCReply(event, Constants.IPC_Download_File_Progress, { + Destination: destination, + Progress: progress, + URL: data.URL, + }); + }, error => { + standardIPCReply(event, Constants.IPC_Download_File_Complete, { + Destination: destination, + URL: data.URL, + }, error); + }); + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/FilesystemIPC.js b/src/renderer/ipc/FilesystemIPC.js new file mode 100644 index 0000000..1294e76 --- /dev/null +++ b/src/renderer/ipc/FilesystemIPC.js @@ -0,0 +1,31 @@ +const Constants = require('../../constants'); +const fs = require('fs'); + +const addListeners = (ipcMain, getMainWindow, dialog) => { + ipcMain.on(Constants.IPC_Browse_Directory + '_sync', (event, data) => { + dialog.showOpenDialog(getMainWindow(), { + defaultPath: data.Location, + properties: ['openDirectory'], + title: data.Title, + }, (filePaths) => { + if (filePaths && (filePaths.length > 0)) { + event.returnValue = filePaths[0]; + } else { + event.returnValue = ''; + } + }); + }); + + ipcMain.on(Constants.IPC_Delete_File, (event, data) => { + try { + if (fs.existsSync(data.FilePath)) { + fs.unlinkSync(data.FilePath); + } + } catch (e) { + } + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/MountsIPC.js b/src/renderer/ipc/MountsIPC.js new file mode 100644 index 0000000..28add62 --- /dev/null +++ b/src/renderer/ipc/MountsIPC.js @@ -0,0 +1,249 @@ +const Constants = require('../../constants'); +const fs = require('fs'); +const helpers = require('../../helpers'); +const os = require('os'); + +let expectedUnmount = {}; +let firstMountCheck = true; +let manualMountDetection = {}; +let mountedData = {}; +let mountedLocations = []; + +const clearManualMountDetection = provider => { + if (manualMountDetection[provider]) { + clearInterval(manualMountDetection[provider]); + delete manualMountDetection[provider]; + } +}; + +const monitorMount = (sender, provider, version, pid, location) => { + manualMountDetection[provider] = setInterval(() => { + helpers + .detectRepertoryMounts(version) + .then(result => { + if (result[provider].PID !== pid) { + if (result[provider].PID === -1) { + clearManualMountDetection(provider); + sender.send(Constants.IPC_Unmount_Drive_Reply, { + data: { + Expected: expectedUnmount[provider], + Location: location, + Provider: provider, + Error: Error(provider + ' Unmounted').toString(), + Success: false, + } + }); + } else { + pid = result[provider].PID; + } + } + }) + .catch(e => { + console.log(e); + }); + },6000); +}; + +const unmountAllDrives = () => { + // Reset mount states + for (const provider of Constants.PROVIDER_LIST) { + clearManualMountDetection(provider); + expectedUnmount[provider] = true; + } + + // Unmount all items + for (const i in mountedLocations) { + const data = mountedData[mountedLocations[i]]; + helpers.stopMountProcessSync(data.Version, data.Provider); + } + + mountedLocations = []; + mountedData = {}; +}; + +const addListeners = (ipcMain, setTrayImage, standardIPCReply) => { + ipcMain.on(Constants.IPC_Check_Mount_Location + '_sync', (event, data) => { + let response = { + Success: true, + Error: '' + }; + + try { + if (fs.existsSync(data.Location) && fs.statSync(data.Location).isDirectory()) { + if (fs.readdirSync(data.Location).length !== 0) { + response.Success = false; + response.Error = 'Directory not empty: ' + data.Location; + } + } else { + response.Success = false; + response.Error = 'Directory not found: ' + data.Location; + } + } catch (e) { + response.Success = false; + response.Error = e.toString(); + } + event.returnValue = response; + }); + + ipcMain.on(Constants.IPC_Detect_Mounts, (event, data) => { + let driveLetters = {}; + for (const provider of Constants.PROVIDER_LIST) { + driveLetters[provider] = []; + } + + const grabDriveLetters = (locations) => { + for (let i = 'c'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) { + const drive = (String.fromCharCode(i) + ':').toUpperCase(); + let driveInUse; + if (Object.keys(locations).length > 0) { + for (const provider of Constants.PROVIDER_LIST) { + driveInUse = locations[provider].startsWith(drive); + if (driveInUse) + break; + } + } + if (!driveInUse) { + try { + if (!fs.existsSync(drive)) { + for (const provider of Constants.PROVIDER_LIST) { + driveLetters[provider].push(drive); + } + } + } catch (e) { + } + } + } + + if (Object.keys(locations).length > 0) { + for (const provider of Constants.PROVIDER_LIST) { + if (locations[provider].length > 0) { + if (!driveLetters[provider].find((driveLetter) => { + return driveLetter === locations[provider]; + })) { + driveLetters[provider].push(locations[provider]); + } + } + } + } + }; + + const setImage = (locations) => { + let driveInUse; + if (Object.keys(locations).length > 0) { + for (const provider of Constants.PROVIDER_LIST) { + driveInUse = locations[provider].length > 0; + if (driveInUse) + break; + } + } + + setTrayImage(driveInUse) + }; + + helpers + .detectRepertoryMounts(data.Version) + .then((results) => { + let storageData = {}; + let locations = {}; + for (const provider of Constants.PROVIDER_LIST) { + storageData[provider] = results[provider] ? results[provider] : { + Active: false, + Location: '', + PID: -1, + }; + locations[provider] = storageData[provider].Location; + + if (storageData[provider].PID !== -1) { + expectedUnmount[provider] = false; + if (firstMountCheck) { + monitorMount(event.sender, provider, data.Version, storageData[provider].PID, storageData[provider].Location); + } + } + } + + if (os.platform() === 'win32') { + grabDriveLetters(locations); + } + + setImage(locations); + if (firstMountCheck) { + firstMountCheck = false; + } + standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, { + DriveLetters: driveLetters, + Locations: locations, + }); + }) + .catch(error => { + if (os.platform() === 'win32') { + grabDriveLetters({}); + } + setImage({}); + standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, { + DriveLetters: driveLetters, + }, error); + }); + }); + + ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => { + expectedUnmount[data.Provider] = false; + + if (mountedLocations.indexOf(data.Location) !== -1) { + console.log(data.Provider + ' already mounted: ' + data.Location); + } else { + mountedLocations.push(data.Location); + mountedData[data.Location] = { + Version: data.Version, + Provider: data.Provider, + }; + const errorHandler = (pid, error) => { + if (mountedLocations.indexOf(data.Location) !== -1) { + mountedLocations.splice(mountedLocations.indexOf(data.Location), 1); + delete mountedData[data.Location]; + } + + standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, { + Expected: expectedUnmount[data.Provider], + Location: data.Location, + Provider: data.Provider, + }, error || Error(data.Provider + ' Unmounted')); + }; + helpers + .executeMount(data.Version, data.Provider, data.Location, data.NoConsoleSupported, (error, pid) => { + errorHandler(pid, error); + }) + .then(() => { + standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, { + Provider: data.Provider, + }); + }) + .catch(error => { + errorHandler(-1, error); + }); + } + }); + + ipcMain.on(Constants.IPC_Unmount_All_Drives, (event, data) => { + unmountAllDrives(); + standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply); + }); + + ipcMain.on(Constants.IPC_Unmount_Drive, (event, data) => { + clearManualMountDetection(data.Provider); + + expectedUnmount[data.Provider] = true; + helpers + .stopMountProcess(data.Version, data.Provider) + .then(result => { + console.log(result); + }) + .catch(e => { + console.log(e); + }); + }); +}; + +module.exports = { + addListeners, + unmountAllDrives +}; \ No newline at end of file diff --git a/src/renderer/ipc/PlatformIPC.js b/src/renderer/ipc/PlatformIPC.js new file mode 100644 index 0000000..f55a35c --- /dev/null +++ b/src/renderer/ipc/PlatformIPC.js @@ -0,0 +1,79 @@ +const Constants = require('../../constants'); +const fs = require('fs'); +const helpers = require('../../helpers'); +const os = require('os'); +const path = require('path'); + +let _platformOverride; + +const getPlatformOverride = () => { + return _platformOverride; +}; + +const setPlatformOverride = platformOverride => { + _platformOverride = platformOverride; +}; + +const addListeners = (ipcMain, detectScript, saveUiSettings) => { + ipcMain.on(Constants.IPC_Get_Platform, (event) => { + const sendResponse = (appPlatform, platform) => { + event.sender.send(Constants.IPC_Get_Platform_Reply, { + AppPlatform: appPlatform, + Platform: platform, + }); + }; + + const platform = os.platform(); + if (platform === 'linux') { + if (_platformOverride && (_platformOverride.length > 0)) { + sendResponse(_platformOverride, 'linux'); + } else { + const scriptFile = path.join(os.tmpdir(), 'repertory_detect_linux.sh'); + fs.writeFileSync(scriptFile, detectScript); + + helpers + .executeScript(scriptFile) + .then(data => { + let appPlatform = data.replace(/(\r\n|\n|\r)/gm, ""); + if (appPlatform === 'unknown') { + helpers + .downloadFile(Constants.LINUX_DETECT_SCRIPT_URL, scriptFile, null, err => { + if (err) { + sendResponse(appPlatform, platform); + } else { + helpers + .executeScript(scriptFile) + .then(data => { + appPlatform = data.replace(/(\r\n|\n|\r)/gm, ""); + sendResponse(appPlatform, platform); + }) + .catch(() => { + sendResponse(appPlatform, platform); + }); + } + }); + } else { + sendResponse(appPlatform, platform); + } + }) + .catch(() => { + sendResponse(platform, platform); + }); + } + } else { + sendResponse(platform, platform); + } + }); + + ipcMain.on(Constants.IPC_Set_Linux_AppPlatform, (event, data) => { + setPlatformOverride(data.AppPlatform); + saveUiSettings(); + event.returnValue = true; + }); +}; + +module.exports = { + getPlatformOverride, + setPlatformOverride, + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/ReleaseIPC.js b/src/renderer/ipc/ReleaseIPC.js new file mode 100644 index 0000000..5d1eb66 --- /dev/null +++ b/src/renderer/ipc/ReleaseIPC.js @@ -0,0 +1,87 @@ +const Constants = require('../../constants'); +const fs = require('fs'); +const helpers = require('../../helpers'); +const os = require('os'); +const path = require('path'); +const unzip = require('unzipper'); + +const addListeners = (ipcMain, standardIPCReply) => { + ipcMain.on(Constants.IPC_Check_Installed, (event, data) => { + const destination = path.join(helpers.getDataDirectory(), data.Version); + helpers + .getMissingDependencies(data.Dependencies) + .then((dependencies) => { + let exists = false; + try { + exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory(); + } catch (e) { + } + standardIPCReply(event, Constants.IPC_Check_Installed_Reply, { + Dependencies: dependencies, + Exists: exists, + Version: data.Version, + }); + }).catch(error => { + standardIPCReply(event, Constants.IPC_Check_Installed_Reply, { + Dependencies: [], + Version: data.Version, + }, error); + }); + }); + + ipcMain.on(Constants.IPC_Extract_Release, (event, data) => { + const destination = path.join(helpers.getDataDirectory(), data.Version); + helpers.removeDirectoryRecursively(destination); + helpers.mkDirByPathSync(destination); + + const stream = fs.createReadStream(data.Source); + stream + .pipe(unzip.Extract({ path: destination })) + .on('error', error => { + try { + helpers.removeDirectoryRecursively(destination); + } catch (e) { + } + stream.close(); + standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { + Source: data.Source, + }, error); + }) + .on('finish', () => { + stream.close(); + if (os.platform() !== 'win32') { + helpers + .executeAndWait("chmod +x \"" + path.join(destination, 'repertory') + "\"") + .then(() => { + standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { + Source: data.Source, + }); + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { + Source: data.Source, + }, error); + }) + } else { + standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { + Source: data.Source, + }); + } + }); + }); + + ipcMain.on(Constants.IPC_Test_Release, (event, data) => { + helpers + .testRepertoryBinary(data.Version) + .then(() => { + standardIPCReply(event, Constants.IPC_Test_Release_Reply, {}); + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Test_Release_Reply, {}, error); + }); + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/StateIPC.js b/src/renderer/ipc/StateIPC.js new file mode 100644 index 0000000..f6f1a7c --- /dev/null +++ b/src/renderer/ipc/StateIPC.js @@ -0,0 +1,30 @@ +const Constants = require('../../constants'); +const fs = require('fs'); +const helpers = require('../../helpers'); +const path = require('path'); + +const addListeners = ipcMain => { + ipcMain.on(Constants.IPC_Get_State, event => { + helpers.mkDirByPathSync(helpers.getDataDirectory()); + const configFile = path.join(helpers.getDataDirectory(), 'settings.json'); + if (fs.existsSync(configFile)) { + event.sender.send(Constants.IPC_Get_State_Reply, { + data: JSON.parse(fs.readFileSync(configFile, 'utf8')), + }); + } else { + event.sender.send(Constants.IPC_Get_State_Reply, { + data: null, + }); + } + }); + + ipcMain.on(Constants.IPC_Save_State, (event, data) => { + helpers.mkDirByPathSync(helpers.getDataDirectory()); + const configFile = path.join(helpers.getDataDirectory(), 'settings.json'); + fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8'); + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/SystemIPC.js b/src/renderer/ipc/SystemIPC.js new file mode 100644 index 0000000..52ebb2f --- /dev/null +++ b/src/renderer/ipc/SystemIPC.js @@ -0,0 +1,16 @@ +const Constants = require('../../constants'); +const os = require('os'); +const helpers = require('../../helpers'); + +const addListeners = (ipcMain, closeApplication) => { + ipcMain.on(Constants.IPC_Reboot_System, () => { + if (os.platform() === 'win32') { + helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']); + } + closeApplication(); + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file diff --git a/src/renderer/ipc/UpgradeIPC.js b/src/renderer/ipc/UpgradeIPC.js new file mode 100644 index 0000000..7bdc24c --- /dev/null +++ b/src/renderer/ipc/UpgradeIPC.js @@ -0,0 +1,116 @@ +const Constants = require('../../constants'); +const fs = require('fs'); +const helpers = require('../../helpers'); +const os = require('os'); + +const addListeners = (ipcMain, setIsInstalling, unmountAllDrives, standardIPCReply) => { + ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => { + let allowSkipVerification = true; + + unmountAllDrives(); + + let tempSig; + let tempPub; + const cleanupFiles = () => { + try { + if (tempSig) { + fs.unlinkSync(tempSig); + } + if (tempPub) { + fs.unlinkSync(tempPub); + } + } catch (e) { + } + }; + + const errorHandler = err => { + cleanupFiles(); + setIsInstalling(false); + standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { + AllowSkipVerification: allowSkipVerification, + Source: data.Source, + }, err); + }; + + // TODO Enable verification in 1.0.4 + const hasSignature = false;//!data.SkipVerification && data.Signature && (data.Signature.length > 0); + const hasHash = false;//!data.SkipVerification && data.Sha256 && (data.Sha256.length > 0); + if (hasSignature) { + try { + const files = helpers.createSignatureFiles(data.Signature, Constants.DEV_PUBLIC_KEY); + tempPub = files.PublicKeyFile; + tempSig = files.SignatureFile; + } catch (e) { + errorHandler(e); + return; + } + } + + let command; + let args; + const platform = os.platform(); + if (platform === 'win32') { + command = data.Source; + } else if (platform === 'darwin') { + command = 'open'; + args = ['-a', 'Finder', data.Source]; + } else if (platform === 'linux') { + try { + command = data.Source; + fs.chmodSync(command, '750'); + } catch (e) { + errorHandler(e); + } + } else { + errorHandler(Error('Platform not supported: ' + os.platform())); + } + + if (command) { + const executeInstall = () => { + setIsInstalling(true); + helpers + .executeAsync(command, args) + .then(() => { + cleanupFiles(); + standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply) + }) + .catch(error => { + setIsInstalling(false); + errorHandler(error); + }); + }; + + if (hasSignature) { + helpers + .verifySignature(data.Source, tempSig, tempPub) + .then(() => { + executeInstall(); + }) + .catch(() => { + errorHandler(Error('Failed to verify installation package signature')); + }); + } else if (hasHash) { + helpers + .verifyHash(data.Source, data.Sha256) + .then(()=> { + executeInstall(); + }) + .catch(() => { + errorHandler(Error('Failed to verify installation package hash')); + }); + } else { + if (platform === 'darwin') { + setTimeout(executeInstall, 3000); + } else { + executeInstall(); + } + } + } else { + errorHandler(Error('Unsupported upgrade: ' + data.Source)); + } + }); +}; + +module.exports = { + addListeners +}; \ No newline at end of file