Some, a lot changes to 1.4.10

This commit is contained in:
WolverinDEV 2020-08-22 21:33:30 +02:00
parent f3add08a92
commit 02d683954b
14 changed files with 907 additions and 356 deletions

2
github

@ -1 +1 @@
Subproject commit 2d7be74482f4bec8f06810b3cd8406785ebfcfa1
Subproject commit 16aeb7e7c75fb3af0b5238a093074497d02ec7c4

34
main.ts
View File

@ -1,10 +1,30 @@
import * as electron from "electron";
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";
import * as Sentry from "@sentry/electron";
/*
Sentry.init({
dsn: "https://72b9f40ce6894b179154e7558f1aeb87@o437344.ingest.sentry.io/5399791",
appName: "TeaSpeak - Client",
release: "TeaClient@" + electron.app.getVersion()
});
*/
/* just swallow this... */
process.on('uncaughtException', err => {
console.error('Uncaught Exception thrown: %o', err);
electron.app.whenReady().then(() => {
electron.dialog.showMessageBox({
type: "error",
message: "An uncaught exception has reached the stack root.\nClosing application.",
title: "Caught an uncaught exception"
}).then(() => {
process.exit(1);
});
});
});
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);
@ -41,10 +61,4 @@ if(process_arguments.length > 0 && process_arguments[0] === "crash-handler") {
const tea_client = require("./modules/core/main.js");
tea_client.execute();
}
/* just swallow this... */
process.on('uncaughtException', function (err) {
console.error(err);
app.exit(1);
});
}

View File

@ -9,7 +9,6 @@ import MessageBoxOptions = electron.MessageBoxOptions;
import {process_args, parse_arguments, Arguments} from "../shared/process-arguments";
import {open as open_changelog} from "./app-updater/changelog";
import * as crash_handler from "../crash_handler";
import {open_preview} from "./url-preview";
async function execute_app() {
if(process_args.has_value("update-execute")) {
@ -156,29 +155,26 @@ async function execute_app() {
}
function main() {
process.on('uncaughtException', err => {
console.error('Uncaught Exception thrown:');
console.dir(err);
process.exit(1);
});
//setTimeout(() => process.crash(), 1000);
if(app) { //We're executed!
parse_arguments();
if(process_args.has_value(Arguments.DISABLE_HARDWARE_ACCELERATION))
app.disableHardwareAcceleration();
if('allowRendererProcessReuse' in app)
app.allowRendererProcessReuse = false;
if(process_args.has_value(Arguments.DUMMY_CRASH_MAIN))
crash_handler.handler.crash();
parse_arguments();
if(process_args.has_value(Arguments.DISABLE_HARDWARE_ACCELERATION))
app.disableHardwareAcceleration();
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);
}
if(process_args.has_value(Arguments.DUMMY_CRASH_MAIN))
crash_handler.handler.crash();
app.on('second-instance', (event, argv, workingDirectory) => instance_handler.handle_second_instance_call(argv, workingDirectory));
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('ready', execute_app);
app.on('second-instance', (event, argv, workingDirectory) => instance_handler.handle_second_instance_call(argv, workingDirectory));
}
app.on('ready', execute_app);
}
export const execute = main;

View File

@ -85,16 +85,6 @@ export namespace ui {
const WINDOW_WIDTH = 340 + (dev_tools ? 1000 : 0);
const WINDOW_HEIGHT = 400 + (process.platform == "win32" ? 40 : 0);
let bounds = screen.getPrimaryDisplay()?.bounds;
let x, y;
if(bounds) {
x = (bounds.x | 0) + ((bounds.width | 0) - WINDOW_WIDTH) / 2;
y = (bounds.y | 0) + ((bounds.height | 0) - WINDOW_HEIGHT) / 2;
} else {
x = 0;
y = 0;
}
gui = new electron.BrowserWindow({
width: WINDOW_WIDTH,
height: WINDOW_HEIGHT,
@ -121,9 +111,23 @@ export namespace ui {
gui.on('ready-to-show', () => {
gui.show();
console.log("Setting UI position to %ox%o", x, y);
if(typeof x === "number" && typeof y === "number")
gui.setPosition(x, y);
try {
let bounds = screen.getPrimaryDisplay()?.bounds;
let x, y;
if(bounds) {
x = (bounds.x | 0) + ((bounds.width | 0) - WINDOW_WIDTH) / 2;
y = (bounds.y | 0) + ((bounds.height | 0) - WINDOW_HEIGHT) / 2;
} else {
x = 0;
y = 0;
}
console.log("Setting UI position to %ox%o", x, y);
if(typeof x === "number" && typeof y === "number")
gui.setPosition(x, y);
} catch (error) {
console.warn("Failed to apply UI position: %o", error);
}
loadWindowBounds('ui-load-window', gui, undefined, { applySize: false }).then(() => {
startTrackWindowBounds('ui-load-window', gui);

View File

@ -1,5 +1,3 @@
import {main_window} from "../core/main_window";
require("../shared/require").setup_require(module);
import {app, BrowserWindow, remote} from "electron";
import * as path from "path";
@ -80,6 +78,8 @@ export function handle_crash_callback(args: string[]) {
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
}
export const handler = require( "teaclient_crash_handler");
if(typeof window === "object")
(window as any).crash = handler;
export function initialize_handler(component_name: string, requires_file: boolean) {
const start_path = requires_file ? (" " + path.join(__dirname, "..", "..")) : "";

View File

@ -42,9 +42,12 @@ export function initialize(manifestTarget: string) {
loader.register_task(loader.Stage.JAVASCRIPT, {
name: "teaclient jquery",
function: async () => {
window.$ = require("jquery");
window.jQuery = window.$;
Object.assign(window.$, window.jsrender = require('jsrender'));
//const jquery = require("jquery");
//console.error(jquery);
//window.$ = jquery;
//window.jQuery = window.$;
//Object.assign(window.$, window.jsrender = require('jsrender'));
},
priority: 80
});

View File

@ -1,71 +1,129 @@
import {AbstractCommandHandler, AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler";
import {
AbstractServerConnection, CommandOptionDefaults, CommandOptions,
AbstractServerConnection,
CommandOptionDefaults,
CommandOptions,
ConnectionStateListener,
ServerCommand
} from "tc-shared/connection/ConnectionBase";
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
import {tr} from "tc-shared/i18n/localize";
import {ConnectionHandler, ConnectionState, DisconnectReason} from "tc-shared/ConnectionHandler";
import {NativeServerConnection, ServerType, spawn_server_connection as spawn_native_server_connection, destroy_server_connection as destroy_native_server_connection} from "tc-native/connection";
import {
destroy_server_connection as destroy_native_server_connection,
NativeServerConnection,
ServerType,
spawn_server_connection as spawn_native_server_connection
} from "tc-native/connection";
import {ConnectionCommandHandler} from "tc-shared/connection/CommandHandler";
import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler";
import {ServerAddress} from "tc-shared/ui/server";
import {TeaSpeakHandshakeHandler} from "tc-shared/profiles/identities/TeamSpeakIdentity";
import {VoiceConnection} from "./VoiceConnection";
import {AbstractVoiceConnection} from "tc-shared/connection/VoiceConnection";
import {LogCategory, logDebug, logWarn} from "tc-shared/log";
import {ErrorCode} from "tc-shared/connection/ErrorCode";
interface ErrorCodeListener {
callback: (result: CommandResult) => void;
code: string;
command: string;
timeout: number;
}
class ErrorCommandHandler extends AbstractCommandHandler {
private _handle: ServerConnection;
private readonly handle: ServerConnection;
private errorCodeMapping: {[key: string]: ErrorCodeListener} = {};
private errorCodeHistory: ErrorCodeListener[] = [];
private errorCodeIndex = 0;
constructor(handle: ServerConnection) {
super(handle);
this._handle = handle;
this.handle = handle;
}
generateReturnCode(command: string, callback: (result: CommandResult) => void) : string {
const listener = {
callback: callback,
code: "rt-" + (++this.errorCodeIndex),
timeout: 0,
command: command
} as ErrorCodeListener;
listener.timeout = setTimeout(() => {
delete this.errorCodeMapping[listener.code];
const index = this.errorCodeHistory.indexOf(listener);
if(index !== -1) {
this.errorCodeHistory.splice(index, 1);
}
logWarn(LogCategory.NETWORKING, tr("Command %s timeout out."), command);
callback(new CommandResult([{ id: ErrorCode.COMMAND_TIMED_OUT, msg: "timeout" }]));
}, 5000) as any;
this.errorCodeMapping[listener.code] = listener;
this.errorCodeHistory.push(listener);
return listener.code;
}
handle_command(command: ServerCommand): boolean {
if(command.command === "error") {
const return_listener: {[key: string]: (result: CommandResult) => any} = this._handle["_return_listener"];
const data = command.arguments[0];
let return_code : string = data["return_code"];
if(!return_code) {
const listener = return_listener["last_command"] || return_listener["_clientinit"];
if(typeof(listener) === "function") {
console.warn(tr("Received error without return code. Using last command (%o)"), listener);
listener(new CommandResult(command.arguments));
delete return_listener["last_command"];
delete return_listener["_clientinit"];
} else {
console.warn(tr("Received error without return code."), data);
const returnCode = data["return_code"];
let codeListener: ErrorCodeListener;
if(!returnCode) {
const [ code ] = this.errorCodeHistory.splice(0, 1);
if(!code) {
logWarn(LogCategory.NETWORKING, tr("Received error without a return code and we're not expecting an error."));
return true;
}
return false;
logDebug(LogCategory.NETWORKING, tr("Received error without any error code. Using the first send command %s (%s)"), code.command, code.code);
codeListener = code;
} else {
let code = this.errorCodeMapping[returnCode];
if(!code) {
logWarn(LogCategory.NETWORKING, tr("Received error for invalid return code %s"), returnCode);
return true;
}
const index = this.errorCodeHistory.indexOf(code);
if(index !== -1) this.errorCodeHistory.splice(index, 1);
codeListener = code;
}
if(return_listener[return_code]) {
return_listener[return_code](new CommandResult(command.arguments));
} else {
console.warn(tr("Error received for no handler! (%o)"), data);
}
delete this.errorCodeMapping[codeListener.code];
clearTimeout(codeListener.timeout);
codeListener.callback(new CommandResult(command.arguments));
return true;
} else if(command.command == "initivexpand") {
if(command.arguments[0]["teaspeak"] == true) {
console.log("Using TeaSpeak identity type");
this._handle.handshake_handler().startHandshake();
this.handle.handshake_handler().startHandshake();
}
return true;
} else if(command.command == "initivexpand2") {
/* its TeamSpeak or TeaSpeak with experimental 3.1 and not up2date */
this._handle["_do_teamspeak"] = true;
this.handle["_do_teamspeak"] = true;
} else if(command.command == "initserver") {
const return_listener: {[key: string]: (result: CommandResult) => any} = this._handle["_return_listener"];
if(typeof(return_listener["_clientinit"]) === "function") {
return_listener["_clientinit"](new CommandResult([{id: 0, message: ""}]));
delete return_listener["_clientinit"];
/* just if clientinit error did not fired (TeamSpeak) */
while(this.errorCodeHistory.length > 0) {
const listener = this.errorCodeHistory.pop();
listener.callback(new CommandResult([{id: 0, message: ""}]));
clearTimeout(listener.timeout);
}
this.errorCodeMapping = {};
} else if(command.command == "notifyconnectioninforequest") {
this._handle.send_command("setconnectioninfo",
this.handle.send_command("setconnectioninfo",
{
//TODO calculate
connection_ping: 0.0000,
@ -108,20 +166,17 @@ class ErrorCommandHandler extends AbstractCommandHandler {
export class ServerConnection extends AbstractServerConnection {
private _native_handle: NativeServerConnection;
private _voice_connection: VoiceConnection;
private readonly _voice_connection: VoiceConnection;
private _do_teamspeak: boolean;
private _return_listener: {[key: string]: (result: CommandResult) => any} = {};
private _command_handler: NativeConnectionCommandBoss;
private _command_error_handler: ErrorCommandHandler;
private _command_handler_default: ConnectionCommandHandler;
private readonly _command_handler: NativeConnectionCommandBoss;
private readonly _command_error_handler: ErrorCommandHandler;
private readonly _command_handler_default: ConnectionCommandHandler;
private _remote_address: ServerAddress;
private _handshake_handler: HandshakeHandler;
private _return_code_index: number = 0;
onconnectionstatechanged: ConnectionStateListener;
constructor(props: ConnectionHandler) {
@ -137,8 +192,7 @@ export class ServerConnection extends AbstractServerConnection {
this._native_handle = spawn_native_server_connection();
this._native_handle.callback_disconnect = reason => {
this.client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
reason: reason,
event: event
reason: reason
});
};
this._native_handle.callback_command = (command, args, switches) => {
@ -243,10 +297,6 @@ export class ServerConnection extends AbstractServerConnection {
return this._command_handler;
}
private generate_return_code() : string {
return (this._return_code_index++).toString();
}
send_command(command: string, data?: any, _options?: CommandOptions): Promise<CommandResult> {
if(!this.connected()) {
console.warn(tr("Tried to send a command without a valid connection."));
@ -257,33 +307,25 @@ export class ServerConnection extends AbstractServerConnection {
Object.assign(options, CommandOptionDefaults);
Object.assign(options, _options);
data = $.isArray(data) ? data : [data || {}];
if(data.length == 0) /* we require min one arg to append return_code */
data = Array.isArray(data) ? data : [data || {}];
if(data.length == 0) { /* we require min one arg to append return_code */
data.push({});
}
let return_code = data[0]["return_code"] !== undefined ? data[0].return_code : this.generate_return_code();
data[0]["return_code"] = return_code;
console.log("Sending %s (%o)", command, data);
console.log("Send: %o %o", command, data);
const promise = new Promise<CommandResult>((resolve, reject) => {
const timeout_id = setTimeout(() => {
delete this._return_listener[return_code];
reject("timeout");
}, 5000);
this._return_listener[return_code] = result => {
clearTimeout(timeout_id);
delete this._return_listener[return_code];
(result.success ? resolve : reject)(result);
};
if(command == "clientinit")
this._return_listener["_clientinit"] = this._return_listener[return_code]; /* fix for TS3 (clientinit does not accept a return code) */
data[0]["return_code"] = this._command_error_handler.generateReturnCode(command, result => {
if(result.success) {
resolve(result);
} else {
reject(result);
}
});
try {
this._native_handle.send_command(command, data, options.flagset || []);
} catch(error) {
reject(tr("failed to send command"));
console.warn(tr("Failed to send command: %o"), error);
}
});

View File

@ -1,10 +1,23 @@
/* --------------- bootstrap --------------- */
import * as RequireProxy from "./RequireProxy";
import * as crash_handler from "../crash_handler";
import {Arguments, parse_arguments, process_args} from "../shared/process-arguments";
import * as path from "path";
import * as Sentry from "@sentry/electron";
import * as electron from "electron";
import {remote} from "electron";
/*
Sentry.init({
dsn: "https://72b9f40ce6894b179154e7558f1aeb87@o437344.ingest.sentry.io/5399791",
appName: "TeaSpeak - Client",
release: "TeaClient@" + electron.remote.app.getVersion()
});
*/
/* first of all setup crash handler */
{
parse_arguments();
if(!process_args.has_flag(Arguments.NO_CRASH_RENDERER)) {
const is_electron_run = process.argv[0].endsWith("electron") || process.argv[0].endsWith("electron.exe");
crash_handler.initialize_handler("renderer", is_electron_run);
}
@ -12,9 +25,6 @@ import * as path from "path";
RequireProxy.initialize(path.join(__dirname, "backend-impl"));
/* --------------- main initialize --------------- */
import {Arguments, parse_arguments, process_args} from "../shared/process-arguments";
import * as electron from "electron";
import {remote} from "electron";
import * as loader from "tc-loader";
import ipcRenderer = electron.ipcRenderer;
@ -93,7 +103,6 @@ loader.register_task(loader.Stage.INITIALIZING, {
loader.register_task(loader.Stage.INITIALIZING, {
name: "teaclient initialize arguments",
function: async () => {
parse_arguments();
if(process_args.has_value(Arguments.DUMMY_CRASH_RENDERER))
crash_handler.handler.crash();

View File

@ -18,6 +18,7 @@ export class Arguments {
static readonly NO_SINGLE_INSTANCE = ["no-single-instance"];
static readonly DUMMY_CRASH_MAIN = ["dummy-crash-main"];
static readonly DUMMY_CRASH_RENDERER = ["dummy-crash-renderer"];
static readonly NO_CRASH_RENDERER = ["no-crash-renderer"];
has_flag: (...keys: (string | string[])[]) => boolean;
has_value: (...keys: (string | string[])[]) => boolean;

View File

@ -111,6 +111,5 @@ LONG WINAPI unhandled_handler(struct _EXCEPTION_POINTERS* apExceptionInfo) {
return EXCEPTION_CONTINUE_SEARCH;
create_minidump(apExceptionInfo);
exit(1);
return EXCEPTION_EXECUTE_HANDLER;
}

View File

@ -46,8 +46,8 @@ NAN_METHOD(query_connect_address) {
return;
}
auto host = Nan::Utf8String{info[0]->ToString()};
auto port = info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->NumberValue();
auto host = Nan::Utf8String{info[0]->ToString(Nan::GetCurrentContext()).ToLocalChecked()};
auto port = info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->NumberValue(Nan::GetCurrentContext()).FromMaybe(0);
auto js_callback = make_unique<Nan::Callback>(info[2].As<v8::Function>());
auto begin = chrono::system_clock::now();

View File

@ -399,7 +399,7 @@ NAN_METHOD(TransferFileTarget::create) {
return;
}
auto instance = make_shared<TransferFileTarget>(*Nan::Utf8String{info[0]}, *Nan::Utf8String{info[1]}, info[2]->IntegerValue());
auto instance = make_shared<TransferFileTarget>(*Nan::Utf8String{info[0]}, *Nan::Utf8String{info[1]}, info[2]->IntegerValue(Nan::GetCurrentContext()).FromMaybe(0));
auto object_wrap = new TransferObjectWrap(instance);
auto object = Nan::NewInstance(Nan::New(TransferObjectWrap::constructor()), 0, nullptr).ToLocalChecked();
object_wrap->do_wrap(object);

954
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,7 @@
"typescript": "^3.9.5"
},
"dependencies": {
"@sentry/electron": "^1.5.2",
"@types/minimist": "^1.2.0",
"@types/ssh2": "^0.5.43",
"argparse": "^1.0.10",
@ -57,7 +58,9 @@
"aws4": "^1.10.0",
"color.js": "^0.1.3",
"electron": "8.0.0",
"electron-installer-windows": "^1.1.0",
"electron-rebuild": "^1.11.0",
"electron-wix-msi": "^2.1.1",
"extend": "^3.0.2",
"extsprintf": "^1.4.0",
"fs-extra": "^9.0.1",