diff --git a/installer/win/Symphony-x64.aip b/installer/win/Symphony-x64.aip
index 7cad63ce..1efdb9a9 100644
--- a/installer/win/Symphony-x64.aip
+++ b/installer/win/Symphony-x64.aip
@@ -73,6 +73,9 @@
+
+
+
@@ -130,13 +133,10 @@
-
-
-
@@ -147,12 +147,15 @@
-
+
+
+
+
@@ -243,20 +246,17 @@
-
-
+
-
-
+
-
-
+
@@ -279,7 +279,7 @@
-
+
diff --git a/installer/win/Symphony-x86.aip b/installer/win/Symphony-x86.aip
index 3662d897..74064d64 100644
--- a/installer/win/Symphony-x86.aip
+++ b/installer/win/Symphony-x86.aip
@@ -72,6 +72,9 @@
+
+
+
@@ -127,14 +130,10 @@
-
-
-
-
@@ -145,12 +144,15 @@
-
+
+
+
+
@@ -215,7 +217,7 @@
-
+
@@ -240,23 +242,17 @@
-
-
-
+
-
-
-
+
-
-
-
+
@@ -280,7 +276,7 @@
-
+
diff --git a/js/search/search.js b/js/search/search.js
index 19156c26..be7f7d8c 100644
--- a/js/search/search.js
+++ b/js/search/search.js
@@ -10,6 +10,7 @@ const makeBoundTimedCollector = require('./queue');
const searchConfig = require('./searchConfig');
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
+const { launchAgent, launchDaemon, taskScheduler } = require('./utils/search-launchd.js');
const libSymphonySearch = require('./searchLibrary');
const Crypto = require('../cryptoLib');
@@ -34,6 +35,7 @@ class Search {
this.messageData = [];
this.isRealTimeIndexing = false;
this.crypto = new Crypto(userId, key);
+ initializeLaunchAgent();
this.decryptAndInit();
this.collector = makeBoundTimedCollector(this.checkIsRealTimeIndexing.bind(this),
searchConfig.REAL_TIME_INDEXING_TIME, this.realTimeIndexing.bind(this));
@@ -594,6 +596,78 @@ function readFile(batch) {
});
}
+/**
+ * Creating launch agent for handling the deletion of
+ * index data folder when app crashed or on boot up
+ */
+function initializeLaunchAgent() {
+ let pidValue = process.pid;
+ if (isMac) {
+ createLaunchScript(pidValue, 'clear-data', searchConfig.LIBRARY_CONSTANTS.LAUNCH_AGENT_FILE, function (res) {
+ if (!res) {
+ log.send(logLevels.ERROR, `Launch Agent not created`);
+ }
+ createLaunchScript(null, 'clear-data-boot', searchConfig.LIBRARY_CONSTANTS.LAUNCH_DAEMON_FILE, function (result) {
+ if (!result) {
+ log.send(logLevels.ERROR, `Launch Agent not created`);
+ }
+ launchDaemon(`${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony/clear-data-boot.sh`, function (data) {
+ if (data) {
+ log.send(logLevels.INFO, 'Launch Daemon: Creating successful');
+ }
+ });
+ });
+
+ launchAgent(pidValue, `${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony/clear-data.sh`, function (response) {
+ if (response) {
+ log.send(logLevels.INFO, 'Launch Agent: Creating successful');
+ }
+ });
+ });
+ } else {
+ let folderPath = isDevEnv ? path.join(__dirname, '..', '..', searchConfig.FOLDERS_CONSTANTS.INDEX_FOLDER_NAME) :
+ path.join(searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH, searchConfig.FOLDERS_CONSTANTS.INDEX_FOLDER_NAME);
+ taskScheduler(`${searchConfig.LIBRARY_CONSTANTS.WINDOWS_TASK_FILE}`, folderPath, pidValue, `${searchConfig.LIBRARY_CONSTANTS.WINDOWS_CLEAR_SCRIPT}`);
+ }
+}
+
+/**
+ * Passing the pid of the application and creating the
+ * bash file in the userData folder
+ * @param pid
+ * @param name
+ * @param scriptPath
+ * @param cb
+ */
+function createLaunchScript(pid, name, scriptPath, cb) {
+
+ if (!fs.existsSync(`${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony/`)) {
+ fs.mkdirSync(`${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony/`);
+ }
+
+ fs.readFile(scriptPath, 'utf8', function (err, data) {
+ if (err) {
+ log.send(logLevels.ERROR, `Error reading sh file: ${err}`);
+ cb(false);
+ return;
+ }
+ let result = data;
+ result = result.replace(/dataPath/g, `"${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/${searchConfig.FOLDERS_CONSTANTS.INDEX_FOLDER_NAME}"`);
+ result = result.replace(/scriptPath/g, `${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony/${name}.sh`);
+ if (pid) {
+ result = result.replace(/SymphonyPID/g, `${pid}`);
+ }
+
+ fs.writeFile(`${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony/${name}.sh`, result, 'utf8', function (error) {
+ if (error) {
+ log.send(logLevels.ERROR, `Error writing sh file: ${error}`);
+ return cb(false);
+ }
+ return cb(true);
+ });
+ });
+}
+
/**
* Exporting the search library
* @type {{Search: Search}}
diff --git a/js/search/searchConfig.js b/js/search/searchConfig.js
index dd7f1b38..bab0a58d 100644
--- a/js/search/searchConfig.js
+++ b/js/search/searchConfig.js
@@ -26,12 +26,25 @@ const libraryPath = isMac ? path.join(macLibraryPath, 'libsymphonysearch.dylib')
const userConfigFileName = 'search_users_config.json';
const userConfigFile = isDevEnv ? path.join(__dirname, '..', '..', userConfigFileName) : path.join(userData, userConfigFileName);
+const libraryFolderPath = isMac ? macLibraryPath : winLibraryPath;
+
+const pathToUtils = isDevEnv ? path.join(__dirname, '../../node_modules/electron-utils') : winLibraryPath;
+const launchAgentFile = path.join(libraryFolderPath, 'search-launch-agent.sh');
+const launchDaemonFile = path.join(libraryFolderPath, 'search-launch-daemon.sh');
+const windowsTaskFile = path.join(pathToUtils, isDevEnv ? 'ClearSchTasks/bin/Release/ClearSchTasks.exe' : 'ClearSchTasks.exe');
+const windowsClearScript = path.join(pathToUtils, isDevEnv ? 'ClearOnBoot/bin/Release/ClearOnBoot.exe' : 'ClearOnBoot.exe');
+
const libraryPaths = {
INDEX_VALIDATOR: indexValidatorPath,
LZ4_PATH: lz4Path,
MAC_LIBRARY_FOLDER: macLibraryPath,
WIN_LIBRARY_FOLDER: winLibraryPath,
- SEARCH_LIBRARY_PATH: libraryPath
+ SEARCH_LIBRARY_PATH: libraryPath,
+ LIBRARY_FOLDER_PATH: libraryFolderPath,
+ LAUNCH_AGENT_FILE: launchAgentFile,
+ LAUNCH_DAEMON_FILE: launchDaemonFile,
+ WINDOWS_TASK_FILE: windowsTaskFile,
+ WINDOWS_CLEAR_SCRIPT: windowsClearScript,
};
const folderPaths = {
diff --git a/js/search/utils/search-launchd.js b/js/search/utils/search-launchd.js
new file mode 100644
index 00000000..de6aca4f
--- /dev/null
+++ b/js/search/utils/search-launchd.js
@@ -0,0 +1,105 @@
+const { exec, execSync } = require('child_process');
+const os = require('os');
+const { randomString } = require('./randomString.js');
+const log = require('../../log.js');
+const logLevels = require('../../enums/logLevels.js');
+const Winreg = require('winreg');
+
+/**
+ * Register for creating launch agent
+ * @type {Registry}
+ */
+const regKey = new Winreg({
+ hive: Winreg.HKCU,
+ key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
+});
+
+/**
+ * Clears the data folder on app crash
+ * @param pid
+ * @param script
+ * @param cb (callback)
+ */
+function launchAgent(pid, script, cb) {
+ exec(`sh "${script}" true ${pid}`, (error, stdout, stderr) => {
+ if (error) {
+ log.send(logLevels.ERROR, `Lanuchd: Error creating script ${error}`);
+ return cb(false);
+ }
+ if (stderr) {
+ log.send(logLevels.ERROR, `Lanuchd: Error creating script ${stderr}`);
+ }
+ return cb(true);
+ });
+}
+
+/**
+ * Clears the data folder on boot
+ * @param script
+ * @param cb (callback)
+ */
+function launchDaemon(script, cb) {
+ exec(`sh "${script}" true`, (error, stdout, stderr) => {
+ if (error) {
+ log.send(logLevels.ERROR, `Lanuchd: Error creating script ${error}`);
+ return cb(false);
+ }
+ if (stderr) {
+ log.send(logLevels.ERROR, `Lanuchd: Error creating script ${stderr}`);
+ }
+ return cb(true);
+ });
+}
+
+/**
+ * Windows clears the data folder on app crash
+ * @param script
+ * @param dataFolder
+ * @param pid
+ * @param clearScript
+ */
+function taskScheduler(script, dataFolder, pid, clearScript) {
+ let userName;
+ if (os.userInfo) {
+ userName = os.userInfo().username;
+ } else {
+ try {
+ userName = execSync('whoami').toString().replace(/^.*\\/, '');
+ } catch (e) {
+ log.send(logLevels.WARN, `whoami failed (using randomString): ${e}`);
+ userName = randomString();
+ }
+ }
+ exec(`SCHTASKS /Create /SC MINUTE /TN "SymphonyTask${userName}" /TR "'${script}' '${dataFolder}' 'SymphonyTask${userName}' '${pid}'" /F`, (error, stdout, stderr) => {
+ if (error) {
+ log.send(logLevels.ERROR, `Lanuchd: Error creating task ${error}`);
+ }
+ if (stderr) {
+ log.send(logLevels.WARN, `Lanuchd: Error creating task ${stderr}`);
+ }
+ log.send(logLevels.INFO, `Lanuchd: Creating task successful ${stdout}`);
+ });
+
+ winRegScript(userName, clearScript, dataFolder);
+}
+
+/**
+ * Clear the data folder on user login for first time
+ * @param userName
+ * @param script
+ * @param dataFolder
+ */
+function winRegScript(userName, script, dataFolder) {
+ regKey.set(`SymphonyTask-${userName}`, Winreg.REG_SZ, `"${script}" "${dataFolder}"`, function(err) {
+ if (err !== null) {
+ log.send(logLevels.INFO, `winReg: Creating task failed ${err}`);
+ }
+ log.send(logLevels.INFO, 'winReg: Creating task successful');
+ });
+}
+
+module.exports = {
+ launchAgent,
+ launchDaemon,
+ taskScheduler
+};
\ No newline at end of file
diff --git a/library/search-launch-agent.sh b/library/search-launch-agent.sh
new file mode 100644
index 00000000..4dacf670
--- /dev/null
+++ b/library/search-launch-agent.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+pid=$2
+launchDir=~/Library/LaunchAgents/
+
+if $1; then
+ if [ ! -d "$launchDir" ]; then
+ mkdir "$launchDir"
+ fi
+ launchctl unload ~/Library/LaunchAgents/com.symphony-search.data.plist
+ cat > ~/Library/LaunchAgents/com.symphony-search.data.plist << EOT
+
+
+
+
+ Label
+ com.symphony-search.data.agent
+ ProgramArguments
+
+ /bin/sh
+ scriptPath
+ false
+ SymphonyPID
+
+ RunAtLoad
+
+ StartInterval
+ 60
+ StandardOutPath
+ /dev/null
+ StandardErrorPath
+ /dev/null
+
+
+EOT
+launchctl load ~/Library/LaunchAgents/com.symphony-search.data.plist
+elif ps -p $pid > /dev/null
+then
+ echo true
+else
+ echo false
+ rm -rf dataPath
+ launchctl unload ~/Library/LaunchAgents/com.symphony-search.data.plist
+ rm -rf ~/Library/LaunchAgents/com.symphony-search.data.plist
+fi
\ No newline at end of file
diff --git a/library/search-launch-daemon.sh b/library/search-launch-daemon.sh
new file mode 100644
index 00000000..f325197f
--- /dev/null
+++ b/library/search-launch-daemon.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+launchDir=~/Library/LaunchAgents/
+
+if $1; then
+ if [ ! -d "$launchDir" ]; then
+ mkdir "$launchDir"
+ fi
+ cat > ~/Library/LaunchAgents/com.symphony-search.clear.plist << EOT
+
+
+
+
+ Label
+ com.symphony-search.clear.daemon
+ ProgramArguments
+
+ /bin/sh
+ scriptPath
+ false
+
+ RunAtLoad
+
+ KeepAlive
+
+ StandardOutPath
+ /dev/null
+ StandardErrorPath
+ /dev/null
+
+
+EOT
+else
+ rm -rf dataPath
+fi
\ No newline at end of file
diff --git a/package.json b/package.json
index 6afcfb95..339f4653 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,9 @@
"config/Symphony.config",
"library/libsymphonysearch.dylib",
"library/indexvalidator.exec",
- "library/lz4.exec"
+ "library/lz4.exec",
+ "library/search-launch-agent.sh",
+ "library/search-launch-daemon.sh"
],
"appId": "symphony-electron-desktop",
"mac": {
@@ -124,6 +126,7 @@
"winreg": "1.2.4"
},
"optionalDependencies": {
- "screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.1"
+ "screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.1",
+ "electron-utils": "git+https://github.com/symphonyoss/electron-utils.git#v1.0.5"
}
}
diff --git a/tests/Search.test.js b/tests/Search.test.js
index f8947e82..d66fd90d 100644
--- a/tests/Search.test.js
+++ b/tests/Search.test.js
@@ -11,11 +11,16 @@ let SearchApi;
jest.mock('electron', function() {
return {
app: {
- getPath: mockedGetPath
+ getPath: mockedGetPath,
+ getName: mockedGetName
}
}
});
+function mockedGetName() {
+ return 'Symphony';
+}
+
function mockedGetPath(type) {
if (type === 'exe') {
return executionPath;
@@ -34,6 +39,7 @@ describe('Tests for Search', function() {
let dataFolderPath;
let realTimeIndexPath;
let tempBatchPath;
+ let launchAgent;
let currentDate = new Date().getTime();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
@@ -51,12 +57,12 @@ describe('Tests for Search', function() {
searchConfig = require('../js/search/searchConfig.js');
const { Search } = require('../js/search/search.js');
SearchApi = new Search(userId, key);
-
+ launchAgent = require('../js/search/utils/search-launchd.js');
realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index');
tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes');
dataFolderPath = path.join(userConfigDir, 'data');
if (fs.existsSync(dataFolderPath)) {
- deleteIndexFolders(dataFolderPath)
+ deleteIndexFolders(dataFolderPath);
}
done();
});
@@ -69,7 +75,10 @@ describe('Tests for Search', function() {
if (fs.existsSync(root)) {
fs.unlinkSync(root);
}
-
+ let script = `${searchConfig.FOLDERS_CONSTANTS.USER_DATA_PATH}/.symphony`;
+ if (fs.existsSync(script)) {
+ deleteIndexFolders(script);
+ }
done();
}, 3000);
});