From d0687a26ba8b4c6f2ef0a19785b796490714ebd3 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sun, 27 Oct 2019 22:39:59 +0100 Subject: [PATCH] Some need changes and fixed the updater --- main.ts | 16 ++++ modules/core/app-updater/index.ts | 85 +++++++++++--------- modules/core/instance_handler.ts | 21 +++++ modules/core/main.ts | 25 +++--- modules/core/main_window.ts | 1 + modules/renderer/app_backend.ts | 9 +++ modules/renderer/index.ts | 7 ++ modules/shared/process-arguments/index.ts | 2 +- modules/shared/process-arguments/minimist.ts | 40 ++++----- package.json | 1 + 10 files changed, 137 insertions(+), 70 deletions(-) create mode 100644 modules/core/instance_handler.ts create mode 100644 modules/renderer/app_backend.ts diff --git a/main.ts b/main.ts index c5cb887..c087233 100644 --- a/main.ts +++ b/main.ts @@ -4,12 +4,28 @@ import * as rhelper from "./modules/shared/require"; console.log("Native module path: %s", rhelper.native_module_path()); import * as crash_handler from "./modules/crash_handler"; +import * as child_process from "child_process"; +import {app} from "electron"; const is_electron_run = process.argv[0].endsWith("electron") || process.argv[0].endsWith("electron.exe"); const process_arguments = is_electron_run ? process.argv.slice(2) : process.argv.slice(1); if(process_arguments.length > 0 && process_arguments[0] === "crash-handler") { /* crash handler callback */ crash_handler.handle_crash_callback(process_arguments.slice(1)); +} else if(process_arguments.length > 0 && process_arguments[0] === "dtest") { + console.log("Executing installer"); + try { + let pipe = child_process.spawn("\"C:\\Program Files (x86)\\TeaSpeak\\update-installer.exe\"", [], { + detached: true, + shell: true, + cwd: "C:\\Program Files (x86)\\TeaSpeak", + stdio: "ignore" + }); + } catch(error) { + console.dir(error); + } + + setTimeout(() => app.exit(0), 2000); } else { if(process_arguments.length > 0 && process_arguments[0] == "--main-crash-handler") crash_handler.initialize_handler("main", is_electron_run); diff --git a/modules/core/app-updater/index.ts b/modules/core/app-updater/index.ts index 6c93388..d291556 100644 --- a/modules/core/app-updater/index.ts +++ b/modules/core/app-updater/index.ts @@ -118,31 +118,27 @@ export async function newest_version(current_version: Version, channel?: string) return undefined; } -export async function extract_updater(update_file: string) { +/** + * @param update_file The input file from where the update will get installed + * @return The target executable file + */ +export async function extract_updater(update_file: string) : Promise { if(!fs.existsSync(update_file)) throw "Missing update file!"; - - let parent_path = app.getAppPath(); - if(parent_path.endsWith(".asar")) { - parent_path = path.join(parent_path, "..", ".."); - parent_path = fs.realpathSync(parent_path); - } - - let post_path; - if(os.platform() == "linux") - post_path = parent_path + "/update-installer"; - else - post_path = parent_path + "/update-installer.exe"; + let update_installer = app.getPath('temp') + "/teaclient-update-installer-" + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); + if(os.platform() == "win32") + update_installer += ".exe"; const source = fs.createReadStream(update_file); const extract = tar.extract(); - await new Promise(resolve => { + await new Promise((resolve, reject) => { let updater_found = false; source.on('end', () => { if(!updater_found) { console.error("Failed to extract the updater (Updater hasn't been found!)"); - resolve(); //FIXME use reject! + reject("Updater hasn't been found in bundle"); } + resolve(); }); @@ -151,13 +147,16 @@ export async function extract_updater(update_file: string) { console.log("Got entry " + header.name); if(header.name == "./update-installer" || header.name == "./update-installer.exe") { - console.log("Found updater! (" + header.size + ")"); - console.log("Extracting to %s", post_path); - const s = fs.createWriteStream(post_path); + console.log("Found updater! (" + header.size + " bytes)"); + console.log("Extracting to %s", update_installer); + const s = fs.createWriteStream(update_installer); stream.pipe(s).on('finish', event => { console.log("Updater extracted and written!"); updater_found = true; resolve(); + }).on('error', event => { + console.error("Failed write update file: %o", event); + reject("failed to write file") }); } else { stream.resume(); //Drain the stream @@ -167,6 +166,8 @@ export async function extract_updater(update_file: string) { source.pipe(extract); }); + + return update_installer; } export async function update_updater() : Promise { @@ -466,6 +467,7 @@ export async function execute_update(update_file: string, restart_callback: (cal console.log("Using update file: %s", update_file); const temp_directory = path.join(app.getPath("temp"), "teaclient_update_" + Math.random().toString(36).substring(7)); + let updater_executable; { console.log("Preparing update source directory at %s", temp_directory); try { @@ -486,8 +488,9 @@ export async function execute_update(update_file: string, restart_callback: (cal if(header.type == "directory") { await fs.mkdirp(target_file); } else if(header.type == "file") { + const target_finfo = path.parse(target_file); { - const directory = path.parse(target_file).dir; + const directory = target_finfo.dir; console.debug("Testing for directory: %s", directory); if(!(await util.promisify(ofs.exists)(directory)) || !(await util.promisify(ofs.stat)(directory)).isDirectory()) { console.log("Creating directory %s", directory); @@ -506,6 +509,12 @@ export async function execute_update(update_file: string, restart_callback: (cal .on('error', reject) .on('finish', resolve); }); + + if(target_finfo.name === "update-installer" || target_finfo.name === "update-installer.exe") { + updater_executable = target_file; + console.log("Found update installer: %s", target_file); + } + return; /* success */ } catch(error) { console.error("Failed to extract update file %s: %o", header.name, error); @@ -534,6 +543,10 @@ export async function execute_update(update_file: string, restart_callback: (cal throw "update unpacking failed"; } } + + if(typeof(updater_executable) !== "string" || !(await fs.pathExists(updater_executable))) + throw "missing update installer executable within update package"; + /* the "new" environment should now be available at 'temp_directory' */ console.log("Update unpacked successfully. Building update extractor file."); @@ -555,22 +568,14 @@ export async function execute_update(update_file: string, restart_callback: (cal throw "failed to write update install config file"; } - const update_installer = path.join(application_path, "update-installer" + (os.platform() === "win32" ? ".exe" : "")); - if(!(await fs.pathExists(update_installer))) { - console.error("Missing update installer! Supposed to be at %s", update_installer); - throw "Missing update installer!"; - } else { - console.log("Using update installer located at %s", update_installer); - } - if(os.platform() == "linux") { console.log("Executing update install on linux"); //We have to unpack it later const rest_callback = () => { - console.log("Executing command %s with args %o", update_installer, [log_file, config_file]); + console.log("Executing command %s with args %o", updater_executable, [log_file, config_file]); try { - let result = child_process.spawnSync(update_installer, [log_file, config_file]); + let result = child_process.spawnSync(updater_executable, [log_file, config_file]); if(result.status != 0) { console.error("Failed to execute update installer! Return code: %d", result.status); dialog.showMessageBox({ @@ -621,13 +626,21 @@ export async function execute_update(update_file: string, restart_callback: (cal //We have to unpack it later const rest_callback = () => { - let pipe = child_process.spawn(update_installer, [log_file, config_file], { - detached: true, - cwd: application_path, - stdio: 'ignore', - }); - pipe.unref(); - app.quit(); + console.log("Executing command %s with args %o", updater_executable, [log_file, config_file]); + + try { + const pipe = child_process.spawn(updater_executable, [log_file, config_file], { + detached: true, + shell: true, + cwd: path.dirname(app.getAppPath()), + stdio: "ignore" + }); + pipe.unref(); + app.quit(); + } catch(error) { + console.dir(error); + electron.dialog.showErrorBox("Failed to finalize update", "Failed to finalize update.\nInvoking the update-installer.exe failed.\nLookup the console for more details."); + } }; restart_callback(rest_callback); } diff --git a/modules/core/instance_handler.ts b/modules/core/instance_handler.ts new file mode 100644 index 0000000..bbe0622 --- /dev/null +++ b/modules/core/instance_handler.ts @@ -0,0 +1,21 @@ +import * as electron from "electron"; +import * as main_window from "./main_window"; + +export function handle_second_instance_call(argv: string[], work_dir: string) { + const original_args = argv.slice(1).filter(e => !e.startsWith("--original-process-start-time=") && e != "--allow-file-access-from-files"); + console.log("Second instance: %o", original_args); + + if(!main_window.main_window) { + console.warn("Ignoring second instance call because we haven't yet started"); + return; + } + main_window.main_window.focus(); + + { + const connect_url = argv.find(e => e.startsWith("teaclient://")); + if(connect_url) { + console.log("Received connect url: %s", connect_url); + main_window.main_window.webContents.send('connect', connect_url); + } + } +} \ No newline at end of file diff --git a/modules/core/main.ts b/modules/core/main.ts index e04595c..4bc2bb7 100644 --- a/modules/core/main.ts +++ b/modules/core/main.ts @@ -1,6 +1,7 @@ // Quit when all windows are closed. import * as electron from "electron"; import * as app_updater from "./app-updater"; +import * as instance_handler from "./instance_handler"; import { app } from "electron"; import MessageBoxOptions = electron.MessageBoxOptions; @@ -35,8 +36,6 @@ async function execute_app() { } } - - if(process_args.has_value("update-execute")) { console.log("Executing update " + process_args.value("update-execute")); await app_updater.execute_update(process_args.value("update-execute"), callback => { @@ -153,6 +152,12 @@ async function execute_app() { } } + /* register client a teaclient:// handler */ + if(app.getAppPath().endsWith(".asar")) { + if(!app.setAsDefaultProtocolClient("teaclient", app.getPath("exe"))) + console.error("Failed to setup default teaclient protocol handler"); + } + try { { const version = await app_updater.current_version(); @@ -176,32 +181,26 @@ async function execute_app() { function main() { process.on('uncaughtException', err => { - console.error(err, 'Uncaught Exception thrown'); + console.error('Uncaught Exception thrown:'); console.dir(err); process.exit(1); }); - /* - if(false) { - SegfaultHandler = require('segfault-handler'); - - SegfaultHandler.registerHandler("crash.log"); // With no argument, SegfaultHandler will generate a generic log file name - } - - const SegfaultHandler = require('segfault-handler'); - SegfaultHandler.registerHandler("crash.log"); // With no argument, SegfaultHandler will generate a generic log file name - */ if(app) { //We're executed! parse_arguments(); if(process_args.has_value(Arguments.DISABLE_HARDWARE_ACCELERATION)) app.disableHardwareAcceleration(); + if(process_args.has_value(Arguments.DUMMY_CRASH_MAIN)) crash_handler.handler.crash(); + if(!process_args.has_value(Arguments.DEBUG) && !process_args.has_value(Arguments.NO_SINGLE_INSTANCE)) { if(!app.requestSingleInstanceLock()) { console.log("Another instance is already running. Closing this instance"); app.exit(0); } + + app.on('second-instance', (event, argv, workingDirectory) => instance_handler.handle_second_instance_call(argv, workingDirectory)); } app.on('ready', execute_app); } diff --git a/modules/core/main_window.ts b/modules/core/main_window.ts index f102877..b0f28d5 100644 --- a/modules/core/main_window.ts +++ b/modules/core/main_window.ts @@ -44,6 +44,7 @@ function spawn_main_window(entry_point: string) { }); main_window.on('closed', () => { + app.releaseSingleInstanceLock(); require("./url-preview").close(); main_window = null; prevent_instant_close = false; diff --git a/modules/renderer/app_backend.ts b/modules/renderer/app_backend.ts new file mode 100644 index 0000000..afeff17 --- /dev/null +++ b/modules/renderer/app_backend.ts @@ -0,0 +1,9 @@ +window["require_setup"](module); + +import * as electron from "electron"; +const remote = electron.remote; + +electron.ipcRenderer.on('connect', (event, url) => { + console.log(tr("Received connect event to %s"), url); + console.error(tr("Dropping connect event (Currently not supported)")); +}); \ No newline at end of file diff --git a/modules/renderer/index.ts b/modules/renderer/index.ts index 4c174bd..5f797bd 100644 --- a/modules/renderer/index.ts +++ b/modules/renderer/index.ts @@ -200,6 +200,13 @@ const load_modules = async () => { console.dir(error); throw error; } + try { + require("./app_backend"); + } catch(error) { + console.error("Failed to load renderer app backend"); + console.dir(error); + throw error; + } try { const helper = require("./icon-helper"); await helper.initialize(); diff --git a/modules/shared/process-arguments/index.ts b/modules/shared/process-arguments/index.ts index 2bc8e94..b7cf82c 100644 --- a/modules/shared/process-arguments/index.ts +++ b/modules/shared/process-arguments/index.ts @@ -40,7 +40,7 @@ export function parse_arguments() { const minimist: (args, opts) => T = require("./minimist") as any; let args = minimist(is_electron_run ? process.argv.slice(2) : process.argv.slice(1), { boolean: true, - stopEarly: true + "--": true }) as Arguments; args.has_flag = (...keys) => { for(const key of [].concat(...Array.of(...keys).map(e => Array.isArray(e) ? Array.of(...e) : [e]))) diff --git a/modules/shared/process-arguments/minimist.ts b/modules/shared/process-arguments/minimist.ts index 7bdf35d..d688fcf 100644 --- a/modules/shared/process-arguments/minimist.ts +++ b/modules/shared/process-arguments/minimist.ts @@ -57,7 +57,7 @@ namespace minimist { const parse = (args: string[], options: minimist.Opts) => { options = options || {}; - var flags = { bools : {}, strings : {}, unknownFn: null, allBools: false }; + let flags = { bools : {}, strings : {}, unknownFn: null, allBools: false }; if (typeof options['unknown'] === 'function') { flags.unknownFn = options['unknown']; @@ -71,7 +71,7 @@ const parse = (args: string[], options: minimist.Opts) => { }); } - var aliases = {}; + let aliases = {}; Object.keys(options.alias || {}).forEach(function (key) { aliases[key] = [].concat(options.alias[key]); aliases[key].forEach(function (x) { @@ -88,14 +88,14 @@ const parse = (args: string[], options: minimist.Opts) => { } }); - var defaults = options['default'] || {}; + let defaults = options['default'] || {}; - var argv = { _ : [] }; + let argv = { _ : [] }; Object.keys(flags.bools).forEach(function (key) { setArg(key, defaults[key] === undefined ? false : defaults[key]); }); - var notFlags = []; + let notFlags = []; if (args.indexOf('--') !== -1) { notFlags = args.slice(args.indexOf('--')+1); @@ -147,16 +147,16 @@ const parse = (args: string[], options: minimist.Opts) => { }); } - for (var i = 0; i < args.length; i++) { - var arg = args[i]; + for (let i = 0; i < args.length; i++) { + let arg = args[i]; if (/^--.+=/.test(arg)) { // Using [\s\S] instead of . because js doesn't support the // 'dotall' regex modifier. See: // http://stackoverflow.com/a/1068308/13216 - var m = arg.match(/^--([^=]+)=([\s\S]*)$/); - var key = m[1]; - var value: any = m[2]; + let m = arg.match(/^--([^=]+)=([\s\S]*)$/); + let key = m[1]; + let value: any = m[2]; if (flags.bools[key]) { value = value !== 'false'; } @@ -169,8 +169,8 @@ const parse = (args: string[], options: minimist.Opts) => { } */ else if (/^--.+/.test(arg)) { - var key = arg.match(/^--(.+)/)[1]; - var next = args[i + 1]; + let key = arg.match(/^--(.+)/)[1]; + let next = args[i + 1]; if (next !== undefined && !/^-/.test(next) && !flags.bools[key] && !flags.allBools @@ -187,11 +187,11 @@ const parse = (args: string[], options: minimist.Opts) => { } } else if (/^-[^-]+/.test(arg)) { - var letters = arg.slice(1,-1).split(''); + let letters = arg.slice(1,-1).split(''); - var broken = false; - for (var j = 0; j < letters.length; j++) { - var next = arg.slice(j+2); + let broken = false; + for (let j = 0; j < letters.length; j++) { + let next = arg.slice(j+2); if (next === '-') { setArg(letters[j], next, arg) @@ -221,7 +221,7 @@ const parse = (args: string[], options: minimist.Opts) => { } } - var key = arg.slice(-1)[0]; + let key = arg.slice(-1)[0]; if (!broken && key !== '-') { if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) && !flags.bools[key] @@ -262,7 +262,7 @@ const parse = (args: string[], options: minimist.Opts) => { }); if (options['--']) { - argv['--'] = new Array(); + argv['--'] = []; notFlags.forEach(function(key) { argv['--'].push(key); }); @@ -277,12 +277,12 @@ const parse = (args: string[], options: minimist.Opts) => { }; function hasKey (obj, keys) { - var o = obj; + let o = obj; keys.slice(0,-1).forEach(function (key) { o = (o[key] || {}); }); - var key = keys[keys.length - 1]; + let key = keys[keys.length - 1]; return key in o; } diff --git a/package.json b/package.json index b05aece..424023f 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "start-d1": "electron . --disable-hardware-acceleration --debug -t --gdb -su http://clientapi.teaspeak.dev/ --updater-ui-loader_type=0", "start-n": "electron . -t --disable-hardware-acceleration --no-single-instance -u=https://clientapi.teaspeak.de/ -d --updater-ui-loader_type=0", "start-01": "electron . --updater-channel=test -u=http://dev.clientapi.teaspeak.de/ -d --updater-ui-loader_type=0 --updater-local-version=1.0.1", + "dtest": "electron . dtest", "compile-sass": "sass --update .:.", "compile-tsc": "tsc", "build-linux-64": "node installer/build.js linux",