'use strict'; /* This should only contain functions that can not be pulled easily into Svelte */ /* Exported functions in use: * load_config() * check_hash_file_cache() * download_hash_file_to_cache() * open_hash_file_to_temp() * open_local_file() * kill_processes() * run_osascript() * run_cmd() * get_device_info Local functions in use: * open_local_file() */ const child_process = require('child_process'); const crypto = require('crypto'); const fs = require('fs'); const fs_promises = require('node:fs/promises'); const os = require('os'); const path = require('path'); const { ipcRenderer } = require('electron'); // import psList from 'ps-list'; // const ps_list = require('ps-list'); let home_directory = require('os').homedir(); console.log('Home: '+home_directory); let tmp_directory = require('os').tmpdir(); console.log('Temporary: '+tmp_directory); let app_root_path = path.join(home_directory, 'OSIT/native_app'); // macOS default let config = null; let default_osit_sync_app_root_relative_path = 'OSIT/Speaker Ready System/Admin Share/Custom Applications'; // Mainly for macOS laptops let default_osit_sync_app_root_path = path.join(home_directory, default_osit_sync_app_root_relative_path); // macOS default let default_osit_sync_native_app_relative_path = 'OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/osit_aether.current.app'; // Mainly for macOS laptops let default_native_app_relative_path = 'OSIT/native_app/osit_aether.app'; // Mainly for macOS laptops let default_current_native_app_relative_path = 'OSIT/native_app/osit_aether.current.app'; // Mainly for macOS laptops let test_native_app_relative_path = 'OSIT/native_app/osit_aether cmsc remote.app'; // Mainly for macOS laptops // exports.check_for_native_app_update = function () { // console.log('*** Aether App Native export: check_for_native_app_update() ***'); // // let config_directory = null; // // if (os.platform == 'darwin') { // config_directory = path.join(home_directory, 'OSIT/native_app'); // console.log('macOS config directory: '+config_directory); // // let electron_app_path = path.join(home_directory, default_native_app_relative_path); // let test_stats = fs.statSync(electron_app_path); // console.log(test_stats); // // if (test_stats.ctimeMs < 1684182140759) { // console.log(`This may be an old version!`); // } // } else if (os.platform == 'linux') { // config_directory = path.join(home_directory, '.config/OSIT'); // console.log('Linux config directory: '+config_directory); // } else { // console.log('Unknown OS... Is this Windows?'); // return false; // } // // } exports.load_init_config = function () { console.log('*** Aether App Native export: load_init_config() ***'); let cwd = process.cwd(); console.log(`CWD: ${cwd}`); try { if (cwd == '/') { cwd = home_directory; } let directory_list = fs.readdirSync(cwd) console.log('CWD Contents:', directory_list); } catch (err) { console.error(err); } // let home_directory = require('os').homedir(); // console.log('Home: '+home_directory); // let tmp_directory = require('os').tmpdir(); // console.log('Temporary: '+tmp_directory); // let config = null; let config_directory = null; // let default_config_path = path.join(process.cwd(),'ae_native_app_config.current.json'); let default_config_path = 'ae_native_app_config.current.json'; console.log(`Default Config File (path): ${default_config_path}`); let config_path = null; // Set the config path for macOS or Linux if (os.platform == 'darwin') { // config_directory = path.join(home_directory, 'Library/Application Support/OSIT'); config_directory = path.join(home_directory, 'OSIT/native_app'); console.log('macOS config directory: '+config_directory); let electron_app_path = path.join(home_directory, default_native_app_relative_path); let test_stats = fs.statSync(electron_app_path); console.log(test_stats); } else if (os.platform == 'linux') { config_directory = path.join(home_directory, '.config/OSIT'); console.log('Linux config directory: '+config_directory); } // Look for the config file and copy the default if not found. if (fs.existsSync(config_directory)) { console.log('Config directory found: '+config_directory); } else { fs.mkdirSync(config_directory); console.log('Config directory created: '+config_directory); //default_config_path = path.join(process.cwd(),'ae_native_app_config.current.json'); // config_path = path.join(config_directory, 'ae_native_app_config.json'); // fs.copyFileSync(default_config_path, config_path); // console.log('Default config file copied: '+config_directory); } config_path = path.join(config_directory, 'ae_native_app_config.json'); // Attempt to open the config file. The preferred location is based on the OS's config directory. if (fs.existsSync(config_path)) { console.log(`Config file (ae_native_app_config.json) found under ${config_directory}`); } else if (!fs.existsSync(config_path) && fs.existsSync(default_config_path)) { fs.copyFileSync(default_config_path, config_path); console.log('Default config file copied: '+config_directory); // config = JSON.parse(fs.readFileSync(config_path)); // console.log('Config file read.'); } else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.json'))) { //fs.copyFileSync(default_config_path, config_path); //console.log('Default config file copied: '+config_directory); console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); config_path = path.join(cwd, 'ae_native_app_config.json'); console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); let found_config_path = path.join(cwd, 'ae_native_app_config.json'); fs.copyFileSync(found_config_path, config_path); console.log(`Found config file copied: ${config_directory}`); } else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.current.json'))) { console.log(`Config file (config.json) not found under ${config_directory} or CWD. Using default config in CWD. ${cwd}`); default_config_path = path.join(cwd, 'ae_native_app_config.current.json'); fs.copyFileSync(default_config_path, config_path); console.log(`Default config file copied: ${config_directory}`); } else { console.log('Can not find a config file.'); return false; } let local_config = JSON.parse(fs.readFileSync(config_path)); console.log('Config file read.', local_config); config = local_config; // Do some quick clean up. config.home_directory = home_directory; // From the OS platform config.tmp_directory = tmp_directory; // From the OS platform config.app_root_path = config.app_root_path.replace('[home]', home_directory); config.app_root_path = config.app_root_path.replace('[tmp]', tmp_directory); console.log(`App Root Path: ${config.app_root_path}`); config.local_file_cache_path = config.local_file_cache_path.replace('[home]', home_directory); config.local_file_cache_path = config.local_file_cache_path.replace('[tmp]', tmp_directory); console.log(`Local File Cache Path: ${config.local_file_cache_path}`); if (fs.existsSync(config.local_file_cache_path)) { } else { fs.mkdirSync(config.local_file_cache_path); console.log(`Host file cache directory created: ${config.local_file_cache_path}`); } config.host_file_temp_path = config.host_file_temp_path.replace('[home]', home_directory); config.host_file_temp_path = config.host_file_temp_path.replace('[tmp]', tmp_directory); console.log(`Host file temp path: ${config.host_file_temp_path}`); if (fs.existsSync(config.host_file_temp_path)) { } else { fs.mkdirSync(config.host_file_temp_path); console.log(`Host file temp directory created: ${config.host_file_temp_path}`); } // NOTE: This is not ideal... if (!config.account_id) { if (config.event_id == 'pjrcghqwert' || config.event_id == 'nmBfuGFeR0k') { config.account_id = '_XY7DXtc9MY'; } else if (config.event_id == 'r8c-rr-I4-52') { // CMSC config.account_id = '8Gfxbxr19Nw'; } else if (config.event_id == 'bM4PL-l8j8p') { // LCI config.account_id = 'xFP7AhU8Zlc'; } else if (config.event_id == 'Y7v-Zg-fe-gu') { // NCSD config.account_id = 'FrodrLD7B4w'; } else if (config.event_id == '9jW-Db-SF-wt') { // AAPOR config.account_id = 'j5EBhRDqPuw'; } else if (config.event_id == 'zJP-Ld-A7-I4') { // BGH config.account_id = 'GpLf_bnywCs'; } else { console.log('WARNING WARNING WARNING: The account ID could not be guessed. Using the default of 1. <<< WARNING'); config.account_id = '_XY7DXtc9MY'; } } let import_config_to_ipc_result = ipcRenderer.invoke('import_config', config).then((result) => { console.log('IPC import config finished'); // console.log(result); return result; // Result should be "true" }) //console.log(config); return config; } exports.load_full_config = function (init_config) { console.log('*** Aether App Native export: load_config() ***'); console.log('Init config.', init_config); // config = init_config; config = get_url_cfg(init_config).then((cfg) => { console.log('URL config file downloaded.', cfg); let new_config = init_config; // Update with remote device config settings from API. new_config.account_id = cfg.account_id_random; new_config.event_id = cfg.event_id_random; new_config.event_device_id = cfg.event_device_id; new_config.event_location_id = cfg.event_location_id; new_config.event_session_id = cfg.event_session_id; new_config.check_event_device_loop_period = cfg.check_event_device_loop_period; new_config.check_event_location_loop_period = cfg.check_event_location_loop_period; new_config.check_event_loop_period = cfg.check_event_loop_period; new_config.check_event_session_loop_period = cfg.check_event_session_loop_period; new_config.record_audio = cfg.record_audio; new_config.record_video = cfg.record_video; new_config.recording_path = cfg.recording_path; new_config.recording_path = new_config.recording_path.replace('[home]', home_directory); new_config.recording_path = new_config.recording_path.replace('[tmp]', home_directory); new_config.home_directory = home_directory; // From the OS platform new_config.tmp_directory = tmp_directory; // From the OS platform new_config.app_root_path = new_config.app_root_path.replace('[home]', home_directory); new_config.app_root_path = new_config.app_root_path.replace('[tmp]', tmp_directory); console.log(`App Root Path: ${new_config.app_root_path}`); new_config.local_file_cache_path = new_config.local_file_cache_path.replace('[home]', home_directory); new_config.local_file_cache_path = new_config.local_file_cache_path.replace('[tmp]', tmp_directory); console.log(`Local File Cache Path: ${new_config.local_file_cache_path}`); if (fs.existsSync(new_config.local_file_cache_path)) { } else { fs.mkdirSync(new_config.local_file_cache_path); console.log(`Host file cache directory created: ${new_config.local_file_cache_path}`); } new_config.host_file_temp_path = new_config.host_file_temp_path.replace('[home]', home_directory); new_config.host_file_temp_path = new_config.host_file_temp_path.replace('[tmp]', tmp_directory); console.log(`Host file temp path: ${new_config.host_file_temp_path}`); if (fs.existsSync(new_config.host_file_temp_path)) { } else { fs.mkdirSync(new_config.host_file_temp_path); console.log(`Host file temp directory created: ${new_config.host_file_temp_path}`); } let import_config_to_ipc_result = ipcRenderer.invoke('import_config', new_config).then((result) => { console.log('IPC import config finished'); // console.log(result); return result; // Result should be "true" }) return new_config }) //console.log(config); return config; } async function get_url_cfg(cfg) { let base_url = `${cfg.api_protocol}://${cfg.api_server}:${cfg.api_port}/${cfg.api_path}`; let axios_api = axios.create({ baseURL: base_url, timeout: 60000, // in milliseconds; 60000 = 60 seconds /* other custom settings */ }); // axios_api.defaults.headers = cfg['headers']; // axios.defaults.baseURL = `${cfg.api_protocol}://${cfg.api_server}:${cfg.api_port}/${cfg.api_path}`; axios_api.defaults.headers.common['Access-Control-Allow-Origin'] = cfg.access_control_allow_origin; // '*'; // app_cfg.access_control_allow_origin; axios_api.defaults.headers.common['content-type'] = 'application/json'; axios_api.defaults.headers.common['x-aether-api-key'] = cfg.api_secret_key; // axios_api.defaults.headers.common['x-account-id'] = cfg.account_id; let event_device_id = 'dbgMWS3KEHE'; let endpoint = `/event/device/${event_device_id}`; let params = {'event_device_code': 'asdf'}; let response_data_promise = await axios_api.get( endpoint, { params: params, onDownloadProgress: (progressEvent) => { let percent_completed = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log('GET Data Timestamp:', progressEvent.timeStamp, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); // temp_get_object_percent_completed = percent_completed; } } ) .then(function (response) { console.log(`Response: ${response}`); let return_data = response.data['data']; if (Array.isArray(return_data)) { console.log(`Data result is an array/list. Array length: ${return_data.length}`); } else { console.log(`Data result is a dictionary/object, not an array/list.`); } return return_data; }) .catch(function (error) { console.log(`Base URL: ${base_url} | Endpoint: ${endpoint}`); console.log('Error Message:', error.message); // Is this needed here or below in the in the else portion??? if (error.response) { console.log(`Response Status: ${error.response.status}; Status Text: ${error.response.statusText}`); } else { console.log('Error:', error); } if (error.response && error.response.status === 404) { return null; // Returning null since there were no results } return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. }); return response_data_promise; } // Check for local file // Updated 2022-05-06 exports.check_local_file = async function ({local_file_path, filename}) { console.log('*** Electron framework export: check_local_file() ***'); // console.log('Check for local file'); console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); let full_local_file_path = path.join(local_file_path, filename); console.log(full_local_file_path); if (fs.existsSync(full_local_file_path)) { console.log(`Local file exists: ${full_local_file_path}`); return true; } else { return false; } } // Check local hash file cache // Used by Svelte Event Launcher // NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 // Updated 2022-05-06 exports.check_hash_file_cache = async function ({local_file_cache_path, hash}) { // console.log('*** Electron framework export: check_hash_file_cache() ***'); // console.log('Check local hash file cache'); console.log(`*** Electron framework export: check_hash_file_cache() *** Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); let hash_filename = `${hash}.file`; let subdirectory = hash_filename.substring(0,2); let subdirectory_path = path.join(local_file_cache_path, subdirectory); if (fs.existsSync(subdirectory_path)) { } else { console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); return false; } let hash_file_cache_path = path.join(subdirectory_path, hash_filename); // console.log(hash_file_cache_path); if (fs.existsSync(hash_file_cache_path)) { // console.log(`Hashed file exists in cache: ${hash_file_cache_path}`); return true; } else { console.log(`Hashed file not found in cache: ${hash_file_cache_path}`); return false; } } // Check local hash file cache // Used by Svelte Event Launcher // Updated 2022-10-12 exports.check_hash_file_cache_v2 = async function ({local_file_cache_path, hash, verify_hash=false}) { console.log('*** Aether App Native export: check_hash_file_cache_v2() ***'); console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); if (fs.existsSync(local_file_cache_path)) { } else { // This should not happen. The directory needs to be created. console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); return false; } let hash_filename = `${hash}.file`; let hash_subdirectory = hash_filename.substring(0,2); let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); if (fs.existsSync(subdirectory_path)) { } else { // This should not happen. The subdirectory needs to be created. console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`); return false; } let full_cached_hash_path = path.join(subdirectory_path, hash_filename); if (fs.existsSync(full_cached_hash_path)) { // console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); if (verify_hash) { const file_buffer = fs.readFileSync(full_cached_hash_path); const file_hash_sha256 = crypto.createHash('sha256'); file_hash_sha256.update(file_buffer); const file_hash_sha256_check = file_hash_sha256.digest('hex'); if (file_hash_sha256_check == hash) { console.log('File hash match', file_hash_sha256_check); } else { // This should only happen if the file is actively being downloaded or it is corrupt. console.log('File hash does not match', file_hash_sha256_check); return false; } } return true; } else { console.log(`Hashed file not found in cache: ${full_cached_hash_path}`); return null; } } // Download hash file to cache // Used by Svelte Event Launcher // Updated 2022-05-06 exports.download_hash_file_to_cache = async function ({api_base_url, local_file_cache_path, event_file_id=null, hash=null}) { // console.log('*** Electron framework export: download_hash_file_to_cache() ***'); // console.log('Download hash file to cache'); console.log(`*** Electron framework export: download_hash_file_to_cache() *** Base URL: ${api_base_url}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); let endpoint = `/event/file/${event_file_id}/download`; let hash_filename = `${hash}.file`; let subdirectory = hash_filename.substring(0,2); let subdirectory_path = path.join(local_file_cache_path, subdirectory); if (fs.existsSync(subdirectory_path)) { } else { fs.mkdirSync(subdirectory_path); console.log(`Subdirectory directory created: ${subdirectory_path}`); } let hash_file_cache_path = path.join(subdirectory_path, hash_filename); if (fs.existsSync(hash_file_cache_path)) { if (check_hash) { const file_buffer = fs.readFileSync(hash_file_cache_path); const file_hash_sha256 = crypto.createHash('sha256'); file_hash_sha256.update(file_buffer); const file_hash_sha256_check = file_hash_sha256.digest('hex');S if (file_hash_sha256_check == hash) { console.log('File hash match', file_hash_sha256_check); return true; } else { // This should only happen if the file is actively being downloaded or it is corrupt. console.log('File hash does not match', file_hash_sha256_check); return false; } } } // console.log(`!!!ABOUT TO CALL DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, hash_file_cache_path).then((result) => { if (result) { console.log('IPC download file process finished successfully'); return true; } else if (result == null) { console.log('IPC Download Result (file not found?):', result); return null; } else { console.log('IPC Download Result (file being downloaded or something went wrong):', result); return false; } }); // console.log(`!!!DONE WITH DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); return download_file_result; } // Download hash file to cache // Used by Svelte Event Launcher // Updated 2023-05-26 exports.download_hash_file_to_cache_v2 = async function ({api_base_url, api_base_url_backup, local_file_cache_path, event_file_id=null, hash=null, verify_hash=true, overwrite_existing=false}) { console.log('*** Aether App Native export: download_hash_file_to_cache_v2() ***'); console.log(`Base URL: ${api_base_url}; Base URL Backup: ${api_base_url_backup}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); if (fs.existsSync(local_file_cache_path)) { } else { // This should not happen. The directory needs to be created. console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); return false; } let endpoint = `/event/file/${event_file_id}/download`; let hash_filename = `${hash}.file`; let hash_subdirectory = hash_filename.substring(0,2); let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); if (fs.existsSync(subdirectory_path)) { } else { fs.mkdirSync(subdirectory_path); console.log(`Hashed file subdirectory directory created in cache directory: ${subdirectory_path}`); } let full_cached_hash_path = path.join(subdirectory_path, hash_filename); if (fs.existsSync(full_cached_hash_path)) { console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); if (verify_hash) { const file_buffer = fs.readFileSync(full_cached_hash_path); const file_hash_sha256 = crypto.createHash('sha256'); file_hash_sha256.update(file_buffer); const file_hash_sha256_check = file_hash_sha256.digest('hex'); if (file_hash_sha256_check == hash) { // console.log('File hash match', file_hash_sha256_check); if (overwrite_existing) { console.log('Going to overwrite the existing file even though the hash matches.'); } else { return true; } } else { // This should only happen if the file is actively being copied or it is corrupt. console.log('File hash does not match', file_hash_sha256_check); if (overwrite_existing) { console.log('Going to overwrite the existing file because the hash does not match.'); } else { return false; } } } } const tmp_full_save_path = full_cached_hash_path+'.tmp'; if (fs.existsSync(tmp_full_save_path)) { console.log(`A temp download file was found! ${tmp_full_save_path}`); let stats = null; try { stats = fs.statSync(tmp_full_save_path); // console.log(`File Accessed Last: ${stats.atime}`); // File data last changed (actual contents) console.log(`File Data Last Modified: ${stats.mtime}`); // File data last changed (actual contents) console.log(`File Metadata Last Modified: ${stats.ctime}`); // File metadata last changed (filename, permissions, etc) } catch (error) { console.log(error); } let current_datetime = new Date(); let offset_minutes = 3; // In minutes. 5 minutes of no changes to the file content seems reasonable? Trying with 3 minutes 2022-10-12 let offset_datetime = new Date(current_datetime.getTime() - offset_minutes*60000); // console.log(`Times: ${current_datetime} ${offset_datetime} | File ${stats.mtime}`); if (stats.mtime < offset_datetime) { // console.log(`Marking as expired temp file based on modified datetime. Expire after: ${offset_minutes} minutes`); // overwrite_existing = true; } else { console.log(`Temp download file has not expired yet. Expire after: ${offset_minutes} minutes`); // return false; return 'tmp'; } } console.log(`Trying IPC API download from: ${api_base_url}`); let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then(async (result) => { if (result) { console.log(`IPC API download file process finished successfully: ${hash}`); return true; } else if (result == null) { console.log(`IPC API download result (file not found?):`, result); return null; } else if (result === 'in_progress') { console.log(`IPC API primary download IN PROGRESS: ${hash}`); // return false; // Should return result instead??? return result; } else if (result === 'tmp') { console.log(`IPC API primary download FOUND TMP: ${hash}`); // return false; // Should return result instead??? return result; } else { console.log(`IPC API download result (file being downloaded or something went wrong): ${hash}`, result); return false; // if (api_base_url_backup) { // console.log(`Download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`); // let download_backup_file_result = await ipcRenderer.invoke('download_file', api_base_url_backup, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result_backup) => { // if (result_backup) { // console.log('IPC download file (from backup) process finished successfully'); // return true; // } else if (result_backup == null) { // console.log('IPC Download Result (from backup) (file not found?):', result_backup); // return null; // } else { // console.log('IPC Download Result (from backup) (file being downloaded or something went wrong):', result_backup); // return false; // } // }); // } else { // return false; // } } return true; // }); }).then(async (result) => { if (result === false) { console.log(`IPC API primary download FAILED!`); // Trying again with backup API server. API Backup: ${api_base_url_backup}`); if (api_base_url_backup) { console.log(`IPC API primary download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`); console.log(`Trying IPC API backup download from: ${api_base_url_backup}`); let download_backup_file_result = await ipcRenderer.invoke('download_file', api_base_url_backup, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result_backup) => { if (result_backup) { console.log(`IPC API backup download file (from backup) process finished successfully: ${hash}`); return true; } else if (result_backup == null) { console.log('IPC API backup download result (file not found?):', result_backup); return null; } else { console.log(`IPC API backup download result (file being downloaded or something went wrong): ${hash}`, result_backup); return false; } }); return download_backup_file_result; } else { console.log(`IPC Download FAILED! No backup API server passed. Returning false.`); return false; } } else if (result === 'in_progress') { console.log(`IPC API primary download IN PROGRESS: ${hash}`); return false; // Should return result instead??? } else if (result === 'tmp') { console.log(`IPC API primary download FOUND TMP: ${hash}`); return false; // Should return result instead??? } else { return result; } }); // if (download_file_result === false) { // console.log(`Download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`) // download_file_result = await ipcRenderer.invoke('download_file', api_base_url_backup, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result) => { // if (result) { // console.log('IPC download file (from backup) process finished successfully'); // return true; // } else if (result == null) { // console.log('IPC Download Result (from backup) (file not found?):', result); // return null; // } else { // console.log('IPC Download Result (from backup) (file being downloaded or something went wrong):', result); // return false; // } // }); // } return download_file_result; } // Open cached hash file after copying to temp directory // Used by Svelte Event Launcher // NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 // Updated 2022-05-06 exports.open_hash_file_to_temp = async function ({local_file_cache_path, hash, host_file_temp_path, filename}) { console.log('*** Electron framework export: open_hash_file_to_temp() ***'); // console.log('Open cached hash file after copying to temp directory'); console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); let subdirectory = hash.substring(0,2); let subdirectory_path = path.join(local_file_cache_path, subdirectory); if (fs.existsSync(subdirectory_path)) { } else { console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); return false; } let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { console.log('IPC open hash file to temp finished'); console.log(result); return true; }) // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); // console.log(result); console.log(open_hash_file_to_temp_result); console.log('End: open_hash_file_to_temp()'); if (open_hash_file_to_temp_result) { console.log('File opened successfully'); return true; } else { console.log('File was not opened successfully'); return false; } } // Open cached hash file after copying to temp directory // Used by Svelte Event Launcher // Updated 2022-10-12 exports.open_hash_file_to_temp_v2 = async function ({local_file_cache_path, hash, host_file_temp_path, filename, verify_hash=true}) { console.log('*** Aether App Native export: open_hash_file_to_temp_v2() ***'); console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Local File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); // console.log('Process: Check local hash file cache, Download hash file to cache, and Open cached hash file after copying to temp directory'); if (fs.existsSync(local_file_cache_path)) { } else { // This should not happen. The directory needs to be created. console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); return false; } let hash_filename = `${hash}.file`; let hash_subdirectory = hash_filename.substring(0,2); let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); if (fs.existsSync(subdirectory_path)) { } else { // This should not happen. The subdirectory needs to be created. console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`); return false; } let full_cached_hash_path = path.join(subdirectory_path, hash_filename); if (fs.existsSync(full_cached_hash_path)) { console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); if (verify_hash) { const file_buffer = fs.readFileSync(full_cached_hash_path); const file_hash_sha256 = crypto.createHash('sha256'); file_hash_sha256.update(file_buffer); const file_hash_sha256_check = file_hash_sha256.digest('hex'); if (file_hash_sha256_check == hash) { // console.log('File hash match', file_hash_sha256_check); } else { // This should only happen if the file is actively being downloaded or it is corrupt. console.log('File hash does not match', file_hash_sha256_check); return false; } } } else { console.log(`Hashed file not found in cache: ${full_cached_hash_path}`); return null; } let local_file_cache_path_w_sub = subdirectory_path; // I need to go back and clean up the variable names related file directory and file paths. // NOTE: Setting check_hash to false since by default it was checked above. let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', local_file_cache_path_w_sub, hash, host_file_temp_path, filename, verify_hash=false).then((result) => { console.log('IPC open hash file to temp finished'); if (result) { console.log('Local hash file was opened from temp directory.'); return result; } else { console.log('Local hash file was not opened from the temp directory. Something went wrong.'); console.log(result); return false; } }) // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); // console.log(result); // console.log(open_hash_file_to_temp_result); // console.log('End: open_hash_file_to_temp()'); // if (open_hash_file_to_temp_result) { // console.log('File opened successfully'); // return true; // } else { // console.log('File was not opened successfully'); // return false; // } // let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { // console.log('IPC open hash file to temp finished'); // if (result) { // console.log('Local hash file was opened from temp directory.'); // return result; // } else { // console.log('Local hash file was not opened from the temp directory. Something went wrong.'); // console.log(result); // return false; // } // }) return open_hash_file_to_temp_result; } // Open local file // Used by Svelte Event Launcher // NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 // Updated 2022-03-10 exports.open_local_file = async function ({local_file_path, filename}) { console.log('*** Electron framework export: open_local_file() ***'); // console.log('Open local file'); console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); // let full_local_file_path = path.join(local_file_path, filename); // console.log(full_local_file_path); // if (fs.existsSync(full_local_file_path)) { // console.log(`Local file exists: ${full_local_file_path}`); // // return true; // } else { // return false; // } let open_local_file_result = await ipcRenderer.invoke('open_local_file', local_file_path, filename).then((result) => { console.log('IPC open local file finished'); console.log(result); return true; }) console.log(open_local_file_result); console.log('End: open_local_file()'); if (open_local_file_result) { console.log('File opened successfully'); return true; } else { console.log('File was not opened successfully'); return false; } } // // Check local file cache and download from server if needed. // // Updated 2022-03-09 // // exports.check_file_cache = async function ({local_file_cache_path, event_file_id, hash}) { // exports.check_file_cache = async function ({api_base_url, local_file_cache_path, event_file_id, hash}) { // console.log('*** Electron framework export: check_file_cache() ***'); // // console.log('Check local file cache and download from server if needed.'); // console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); // // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random // let hash_filename = hash+'.file'; // let save_path = path.join(local_file_cache_path, hash_filename); // console.log(save_path); // if (fs.existsSync(save_path)) { // console.log('Hashed file cache already exists: '+save_path); // return true; // } else { // console.log('Hashed file not found in local cache. Downloading file: '+save_path); // let endpoint = `/event/file/${event_file_id}/download`; // let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. // console.log(result); // return new Promise((resolve, reject) => { // ipcRenderer.once('download_file_reply', function(event, response){ // console.log(response); // return response; // }) // resolve(true); // }); // // await ipcRenderer.once('download_file_reply', function(event, response){ // // console.log(response); // // return response; // // }); // // result.then(function (response) { // // console.log('Downloaded!!!???'); // // return true; // // }).catch(function (error) { // // console.log(error); // // return false; // // }); // // return result; // // console.log(result); // // if (result) { // // return true; // // } else { // // return false; // // } // } // } // Check local file cache and download from server if needed. Must use IPC to Main to download file. Set a Promise to wait for download_file_reply. // Updated 2022-03-09 async function check_file_cache({api_base_url, local_file_cache_path, event_file_id, hash}) { console.log('*** Electron framework: check_file_cache() ***'); // console.log('Check local file cache and download from server if needed.'); console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random let hash_filename = hash+'.file'; let save_path = path.join(local_file_cache_path, hash_filename); console.log(save_path); if (fs.existsSync(save_path)) { console.log('Hashed file cache already exists: '+save_path); return true; } else { console.log('Hashed file not found in local cache. Downloading file: '+save_path); let endpoint = `/event/file/${event_file_id}/download`; let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. console.log(result); return new Promise((resolve, reject) => { ipcRenderer.once('download_file_reply', function(event, response){ console.log(response); return response; }) resolve(true); }); // await ipcRenderer.once('download_file_reply', function(event, response){ // console.log(response); // return response; // }); // result.then(function (response) { // console.log('Downloaded!!!???'); // return true; // }).catch(function (error) { // console.log(error); // return false; // }); // return result; // console.log(result); // if (result) { // return true; // } else { // return false; // } } } // IPC to Main: Open local file cache if available. Copy to temp directory with given filename first. // Updated 2022-03-09 async function open_local_file({local_file_cache_path, hash, host_file_temp_path, filename}) { console.log('*** Electron framework: open_local_file() ***'); // console.log('Open local file cache if available. Copy to temp directory with given filename first.'); console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); console.log(local_file_cache_path); console.log(hash); console.log(filename); let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); console.log(result); return true; } // No longer needed? Not referenced as of 2022-10-11 exports.check_file_cache_and_open_local_file = async function ({local_file_cache_path, event_file_id, hash, host_file_temp_path, filename}) { console.log('*** Electron framework: check_file_cache_and_open_local_file() ***'); console.log('Checking the local file cache against the remote server and then opening the local file.'); let check_file_cache_result = check_file_cache({local_file_cache_path: local_file_cache_path, event_file_id: event_file_id, hash: hash}); console.log(check_file_cache_result); if (check_file_cache_result) { let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); console.log(open_local_file_result); return open_local_file_result; } ipcRenderer.once('download_file_reply', function(event, response){ console.log(response); let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); console.log(open_local_file_result); return open_local_file_result; }) } // Kill processes // Signals: HUP (hang up), INT (interrupt), QUIT (quit), ABRT (abort), KILL (non-catchable, non-ignoraable kill), ALRMn (alarm clock), TERM (default; software termination signal) // Updated 2022-05-07 exports.kill_processes = async function ({process_name = null, process_id = null, signal = null}) { console.log('*** Electron framework export: kill_processes() ***'); console.log(process_name); // process_name or grep pattern let cmd = ''; if (os.platform == 'darwin') { if (signal == 'HUP') { cmd = `killall -HUP '${process_name}'`; } else if (signal == 'INT') { cmd = `killall -INT '${process_name}'`; } else if (signal == 'QUIT') { cmd = `killall -QUIT '${process_name}'`; } else if (signal == 'ABRT') { cmd = `killall -ABRT '${process_name}'`; } else if (signal == 'KILL') { cmd = `killall -KILL '${process_name}'`; } else if (signal == 'ALRM') { cmd = `killall -ALRM '${process_name}'`; } else if (signal == 'TERM') { cmd = `killall -TERM '${process_name}'`; } else if (process_id && signal == 'HUP') { cmd = `killall -HUP ${process_id}`; } else if (process_id && signal == 'INT') { cmd = `killall -INT ${process_id}`; } else if (process_id && signal == 'QUIT') { cmd = `killall -QUIT ${process_id}`; } else if (process_id && signal == 'ABRT') { cmd = `killall -ABRT ${process_id}`; } else if (process_id && signal == 'KILL') { cmd = `killall -KILL ${process_id}`; } else if (process_id && signal == 'ALRM') { cmd = `killall -ALRM ${process_id}`; } else if (process_id && signal == 'TERM') { cmd = `killall -TERM ${process_id}`; } else { // cmd = `osascript -e 'quit app "${process_name}" saving no'`; cmd = `osascript -e 'quit application "${process_name}" saving no'`; } } else { cmd = `pkill ${process_name}`; } child_process.exec(cmd, (err, stdout, stdin) => { // if (err) throw err; if (err) console.log(err); console.log(stdout); }); console.log(`Killed processes matching ${process_name}`); if (os.platform == 'darwin') { if (process_name == 'Parallels:Acrobat Reader') { // Regular expression: (Parallels).*(Acrobat Reader) // This will find any process with Parallels and Acrobat Reader in the name cmd = `pkill -i -f '(Parallels).*(Acrobat Reader)'`; child_process.exec(cmd, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); }); console.log('Killed Parallels Acrobat Reader process'); } if (process_name == 'Parallels:PowerPoint') { // Regular expression: (Parallels).*(PowerPoint) // This will find any process with Parallels and PowerPoint in the name cmd = `pkill -i -f '(Parallels).*(PowerPoint)'`; child_process.exec(cmd, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); }); console.log('Killed Parallels PowerPoint process'); } } // let signal = 'SIGTERM'; // 'SIGTERM', 'SIGINT', 'SIGHUP' // process.kill(pid, signal); // process.kill(pid, 0); // Special case test if process exists return true; } // Run raw osascript // Updated 2022-05-07 exports.run_osascript = async function ({cmd=null, interactive=false, language=null, flags='h', program_file=null}) { console.log('*** Electron framework export: run_osascript() ***'); console.log(cmd); if (os.platform == 'darwin') { } else { console.log('Not available for this platform. macOS (darwin) only.'); return false; } let osascript_str = ''; if (Array.isArray(cmd)) { console.log('List of cmd strings'); let cmds_str = ''; for (let i = 0; i < cmd.length; i++) { cmds_str += `-e '${cmd[i]}'`; } osascript_str = `osascript ${cmds_str}` } else if (typeof cmd === 'string') { console.log('Single cmd string'); osascript_str = `osascript -e '${cmd}'`; } else { return false; } if (language) { console.log(`Language: ${language}`); osascript_str = `${osascript_str} -l ${language}`; } if (flags) { console.log(`Flags: ${flags}`); osascript_str = `${osascript_str} -s ${flags}`; } console.log(`OSA Script String: ${osascript_str}`); child_process.exec(osascript_str, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); console.log(stdin); }); console.log('Finished'); return true; } // Run raw command // Updated 2022-05-07 exports.run_cmd = async function ({cmd=null, return_stdout=null, return_stdin=null, sync=null}) { console.log('*** Electron framework export: run_cmd() ***'); console.log(`Command String: ${cmd}`); let result; if (!sync) { result = child_process.exec(cmd, (err, stdout, stdin) => { // if (err) throw err; if (err) { console.log('Error:', err); return false; }; console.log('stdout:', stdout); // console.log('stdin:', stdin); if (return_stdout) { console.log('Finished and returning stdout'); return stdout; } else { console.log('Finished and returning true'); return true; } }); } else { result = child_process.execSync(cmd, (err, stdout, stdin) => { // if (err) throw err; if (err) { console.log('Error:', err); return false; }; console.log('stdout:', stdout); // console.log('stdin:', stdin); if (return_stdout) { console.log('Finished and returning stdout'); return stdout; } else { console.log('Finished and returning true'); return true; } }); } console.log('Result:', result); return result; } // Run raw command sync // Updated 2022-05-07 exports.run_cmd_sync = function ({cmd=null, return_stdout=null, return_stdin=null}) { console.log('*** Electron framework export: run_cmd() ***'); console.log(`Command String: ${cmd}`); let stdout; try { stdout = child_process.execSync(cmd, {encoding: 'utf8'}); console.log('Std Out:', stdout); } catch (err) { console.error('Error:', err); return false; } if (return_stdout) { console.log('Finished and returning stdout'); return stdout; } else { console.log('Finished and returning true'); return true; } // let result; // let stdout; // let stderr; // try { // let { stdout, stderr } = child_process.execSync(cmd, (err, stdout, stdin) => { // // if (err) throw err; // if (err) { // console.log('Error:', err); // return false; // }; // console.log('stdout:', stdout); // // console.log('stdin:', stdin); // if (return_stdout) { // console.log('Finished and returning stdout'); // return stdout; // } else { // console.log('Finished and returning true'); // return true; // } // }); // } catch (err) { // console.error(err); // return false; // } // console.log('Result:', result); // return result; } // Run raw command // Updated 2022-05-25 exports.get_device_info = async function () { console.log('*** Electron framework export: get_device_info() ***'); // https://nodejs.org/api/os.html let data = {}; data['arch'] = os.arch(); data['hostname'] = os.hostname(); data['cpus'] = os.cpus(); data['freemem'] = os.freemem(); data['totalmem'] = os.totalmem(); data['loadavg'] = os.loadavg(); data['networkInterfaces'] = os.networkInterfaces(); data['platform'] = os.platform(); data['release'] = os.release(); data['uptime'] = os.uptime(); data['version'] = os.version(); console.log(data); return data; } // For loading JS file function loadJS(){ // Gives -1 when the given input is not in the string // i.e this file has not been added if(filesAdded.indexOf('script.js') !== -1) return // Head tag var head = document.getElementsByTagName('head')[0] // Creating script element var script = document.createElement('script') script.src = 'script.js' script.type = 'text/javascript' // Adding script element head.append(script) // Adding the name of the file to keep record filesAdded += ' script.js' } // To load CSS file function loadCSS() { if(filesAdded.indexOf('styles.css') !== -1) return var head = document.getElementsByTagName('head')[0] // Creating link element var style = document.createElement('link') style.href = 'styles.css' style.type = 'text/css' style.rel = 'stylesheet' head.append(style); // Adding the name of the file to keep record filesAdded += ' styles.css' } exports.check_and_get_updated_native_app_config = function ({file_path='device_configs/ae_native_app_config.default.json', overwrite=false}) { console.log('*** Aether App Native export: check_and_get_updated_native_app_config() ***'); if (os.platform == 'darwin') { let default_osit_sync_app_config_path = path.join(default_osit_sync_app_root_path, file_path); let default_app_config_path = path.join(app_root_path, 'ae_native_app_config.default.json'); console.log('macOS app root and config directory: '+default_app_config_path); fs.copyFileSync(default_osit_sync_app_config_path, default_app_config_path); if (overwrite) { console.log('Overwriting the current active config file...'); let active_app_config_path = path.join(app_root_path, 'ae_native_app_config.json'); console.log('macOS app root and config directory: '+active_app_config_path); fs.copyFileSync(default_osit_sync_app_config_path, active_app_config_path); } } else if (os.platform == 'linux') { app_root_path = path.join(home_directory, '.config/OSIT'); console.log('Linux config directory: '+app_root_path); return false; } else { console.log('Unknown OS... Is this Windows?'); return false; } return true; }