2018-06-20 17:06:55 +00:00
/// <reference path="log.ts" />
2018-03-24 22:38:01 +00:00
/// <reference path="voice/AudioController.ts" />
2018-02-27 16:20:49 +00:00
/// <reference path="proto.ts" />
/// <reference path="ui/view.ts" />
/// <reference path="settings.ts" />
2018-06-20 17:06:55 +00:00
/// <reference path="ui/frames/SelectedItemInfo.ts" />
2018-02-27 16:20:49 +00:00
/// <reference path="FileManager.ts" />
/// <reference path="permission/PermissionManager.ts" />
/// <reference path="permission/GroupManager.ts" />
2018-07-03 10:33:31 +00:00
/// <reference path="ui/frames/ControlBar.ts" />
2019-02-23 14:07:55 +00:00
/// <reference path="connection/ConnectionBase.ts" />
2018-02-27 16:20:49 +00:00
2018-03-07 18:06:52 +00:00
enum DisconnectReason {
2018-03-07 19:14:36 +00:00
REQUESTED ,
2018-03-07 18:06:52 +00:00
CONNECT_FAILURE ,
CONNECTION_CLOSED ,
CONNECTION_FATAL_ERROR ,
CONNECTION_PING_TIMEOUT ,
CLIENT_KICKED ,
CLIENT_BANNED ,
2018-12-28 14:39:23 +00:00
HANDSHAKE_FAILED ,
2018-03-07 18:06:52 +00:00
SERVER_CLOSED ,
2018-09-24 19:14:36 +00:00
SERVER_REQUIRES_PASSWORD ,
2018-03-07 18:06:52 +00:00
UNKNOWN
}
enum ConnectionState {
UNCONNECTED ,
CONNECTING ,
INITIALISING ,
CONNECTED ,
DISCONNECTING
}
2018-02-27 16:20:49 +00:00
enum ViewReasonId {
VREASON_USER_ACTION = 0 ,
VREASON_MOVED = 1 ,
VREASON_SYSTEM = 2 ,
VREASON_TIMEOUT = 3 ,
VREASON_CHANNEL_KICK = 4 ,
VREASON_SERVER_KICK = 5 ,
VREASON_BAN = 6 ,
VREASON_SERVER_STOPPED = 7 ,
VREASON_SERVER_LEFT = 8 ,
VREASON_CHANNEL_UPDATED = 9 ,
VREASON_EDITED = 10 ,
VREASON_SERVER_SHUTDOWN = 11
}
class TSClient {
channelTree : ChannelTree ;
2019-02-23 13:15:22 +00:00
serverConnection : connection.ServerConnection ;
2019-03-07 14:30:53 +00:00
voiceConnection : VoiceConnection | undefined ;
2018-02-27 16:20:49 +00:00
fileManager : FileManager ;
selectInfo : InfoBar ;
permissions : PermissionManager ;
groups : GroupManager ;
controlBar : ControlBar ;
private _clientId : number = 0 ;
private _ownEntry : LocalClientEntry ;
2019-02-17 15:08:10 +00:00
private _reconnect_timer : NodeJS.Timer ;
private _reconnect_attempt : boolean = false ;
2018-02-27 16:20:49 +00:00
constructor ( ) {
this . selectInfo = new InfoBar ( this , $ ( "#select_info" ) ) ;
this . channelTree = new ChannelTree ( this , $ ( "#channelTree" ) ) ;
2019-02-23 13:15:22 +00:00
this . serverConnection = new connection . ServerConnection ( this ) ;
2018-02-27 16:20:49 +00:00
this . fileManager = new FileManager ( this ) ;
this . permissions = new PermissionManager ( this ) ;
this . groups = new GroupManager ( this ) ;
this . _ownEntry = new LocalClientEntry ( this ) ;
this . controlBar = new ControlBar ( this , $ ( "#control_bar" ) ) ;
this . channelTree . registerClient ( this . _ownEntry ) ;
2019-03-07 14:30:53 +00:00
if ( ! settings . static_global ( Settings . KEY_DISABLE_VOICE , false ) )
this . voiceConnection = new VoiceConnection ( this ) ;
2018-02-27 16:20:49 +00:00
}
setup() {
this . controlBar . initialise ( ) ;
}
2018-12-28 14:39:23 +00:00
startConnection ( addr : string , profile : profiles.ConnectionProfile , name? : string , password ? : { password : string , hashed : boolean } ) {
2019-02-17 15:08:10 +00:00
this . cancel_reconnect ( ) ;
this . _reconnect_attempt = false ;
2018-03-07 19:14:36 +00:00
if ( this . serverConnection )
this . handleDisconnect ( DisconnectReason . REQUESTED ) ;
2018-02-27 16:20:49 +00:00
let idx = addr . lastIndexOf ( ':' ) ;
let port : number ;
let host : string ;
if ( idx != - 1 ) {
2018-03-24 22:38:01 +00:00
port = parseInt ( addr . substr ( idx + 1 ) ) ;
2018-02-27 16:20:49 +00:00
host = addr . substr ( 0 , idx ) ;
} else {
host = addr ;
2018-04-16 18:38:35 +00:00
port = 9987 ;
2018-02-27 16:20:49 +00:00
}
2018-12-05 19:46:33 +00:00
console . log ( tr ( "Start connection to %s:%d" ) , host , port ) ;
2018-06-20 19:05:35 +00:00
this . channelTree . initialiseHead ( addr , { host , port } ) ;
2018-09-24 19:14:36 +00:00
if ( password && ! password . hashed ) {
helpers . hashPassword ( password . password ) . then ( password = > {
2019-02-23 13:15:22 +00:00
/* errors will be already handled via the handle disconnect thing */
this . serverConnection . connect ( { host , port } , new connection . HandshakeHandler ( profile , name , password ) ) ;
2018-09-24 19:14:36 +00:00
} ) . catch ( error = > {
2018-12-15 13:04:29 +00:00
createErrorModal ( tr ( "Error while hashing password" ) , tr ( "Failed to hash server password!<br>" ) + error ) . open ( ) ;
2018-09-24 19:14:36 +00:00
} )
2019-02-23 13:15:22 +00:00
} else {
/* errors will be already handled via the handle disconnect thing */
this . serverConnection . connect ( { host , port } , new connection . HandshakeHandler ( profile , name , password ? password.password : undefined ) ) ;
}
2018-02-27 16:20:49 +00:00
}
getClient ( ) : LocalClientEntry { return this . _ownEntry ; }
2019-03-17 11:15:39 +00:00
getClientId() { return this . _clientId ; }
2018-02-27 16:20:49 +00:00
set clientId ( id : number ) {
this . _clientId = id ;
this . _ownEntry [ "_clientId" ] = id ;
}
2018-03-07 18:06:52 +00:00
get clientId() {
return this . _clientId ;
}
2019-02-23 13:15:22 +00:00
getServerConnection ( ) : connection . ServerConnection { return this . serverConnection ; }
2018-02-27 16:20:49 +00:00
/ * *
* LISTENER
* /
onConnected() {
2018-03-07 18:06:52 +00:00
console . log ( "Client connected!" ) ;
2018-03-07 19:14:36 +00:00
this . channelTree . registerClient ( this . _ownEntry ) ;
2018-04-16 18:38:35 +00:00
settings . setServer ( this . channelTree . server ) ;
2018-03-07 18:06:52 +00:00
this . permissions . requestPermissionList ( ) ;
if ( this . groups . serverGroups . length == 0 )
this . groups . requestGroups ( ) ;
this . controlBar . updateProperties ( ) ;
2019-03-17 11:15:39 +00:00
if ( this . controlBar . channel_subscribe_all )
this . channelTree . subscribe_all_channels ( ) ;
else
this . channelTree . unsubscribe_all_channels ( ) ;
2019-03-07 14:30:53 +00:00
if ( this . voiceConnection && ! this . voiceConnection . current_encoding_supported ( ) )
2018-12-05 19:46:33 +00:00
createErrorModal ( tr ( "Codec encode type not supported!" ) , tr ( "Codec encode type " + VoiceConnectionType [ this . voiceConnection . type ] + " not supported by this browser!<br>Choose another one!" ) ) . open ( ) ; //TODO tr
2018-02-27 16:20:49 +00:00
}
2018-04-18 14:25:10 +00:00
get connected ( ) : boolean {
2019-02-23 13:15:22 +00:00
return this . serverConnection && this . serverConnection . connected ( ) ;
2018-04-18 14:25:10 +00:00
}
2018-05-05 12:58:30 +00:00
private certAcceptUrl() {
2018-12-28 14:39:23 +00:00
const properties = {
2019-01-02 11:18:05 +00:00
connect_default : true ,
2018-12-28 14:39:23 +00:00
connect_profile : this.serverConnection._handshakeHandler.profile.id ,
2019-01-02 11:18:05 +00:00
connect_address : this.serverConnection._remote_address.host + ":" + this . serverConnection . _remote_address . port
2018-12-28 14:39:23 +00:00
} ;
2019-01-02 11:18:05 +00:00
const parameters : string [ ] = [ ] ;
for ( const key in properties )
2019-01-20 17:43:14 +00:00
parameters . push ( key + "=" + encodeURIComponent ( properties [ key ] ) ) ;
2019-01-02 11:18:05 +00:00
2018-05-05 12:58:30 +00:00
// document.URL
let callback = document . URL ;
if ( document . location . search . length == 0 )
2019-01-02 11:18:05 +00:00
callback += "?" + parameters . join ( "&" ) ;
2018-05-05 12:58:30 +00:00
else
2019-01-02 11:18:05 +00:00
callback += "&" + parameters . join ( "&" ) ;
2018-05-05 12:58:30 +00:00
2018-06-20 19:05:35 +00:00
return "https://" + this . serverConnection . _remote_address . host + ":" + this . serverConnection . _remote_address . port + "/?forward_url=" + encodeURIComponent ( callback ) ;
2018-05-05 12:58:30 +00:00
}
2018-03-07 18:06:52 +00:00
handleDisconnect ( type : DisconnectReason , data : any = { } ) {
2019-02-17 15:08:10 +00:00
let auto_reconnect = false ;
2018-03-07 18:06:52 +00:00
switch ( type ) {
2018-03-07 19:14:36 +00:00
case DisconnectReason . REQUESTED :
break ;
2018-03-07 18:06:52 +00:00
case DisconnectReason . CONNECT_FAILURE :
2019-02-17 15:08:10 +00:00
if ( this . _reconnect_attempt ) {
auto_reconnect = true ;
chat . serverChat ( ) . appendError ( tr ( "Connect failed" ) ) ;
break ;
}
2018-12-28 14:39:23 +00:00
console . error ( tr ( "Could not connect to remote host! Exception: %o" ) , data ) ;
2018-03-07 18:06:52 +00:00
2018-10-06 13:13:45 +00:00
if ( native_client ) {
createErrorModal (
2018-12-05 19:46:33 +00:00
tr ( "Could not connect" ) ,
tr ( "Could not connect to remote host (Connection refused)" )
2018-10-06 13:13:45 +00:00
) . open ( ) ;
} else {
2018-12-05 19:46:33 +00:00
//TODO tr
2018-10-06 13:13:45 +00:00
createErrorModal (
2018-12-05 19:46:33 +00:00
tr ( "Could not connect" ) ,
2018-10-06 13:13:45 +00:00
"Could not connect to remote host (Connection refused)<br>" +
"If you're sure that the remote host is up, than you may not allow unsigned certificates.<br>" +
"Click <a href='" + this . certAcceptUrl ( ) + "'>here</a> to accept the remote certificate"
) . open ( ) ;
}
2018-11-03 23:39:29 +00:00
sound . play ( Sound . CONNECTION_REFUSED ) ;
2018-03-07 18:06:52 +00:00
break ;
2018-12-28 14:39:23 +00:00
case DisconnectReason . HANDSHAKE_FAILED :
//TODO sound
console . error ( tr ( "Failed to process handshake: %o" ) , data ) ;
createErrorModal (
tr ( "Could not connect" ) ,
tr ( "Failed to process handshake: " ) + data as string
) . open ( ) ;
break ;
2018-03-07 18:06:52 +00:00
case DisconnectReason . CONNECTION_CLOSED :
2018-12-05 19:46:33 +00:00
console . error ( tr ( "Lost connection to remote server!" ) ) ;
2018-03-07 18:06:52 +00:00
createErrorModal (
2018-12-05 19:46:33 +00:00
tr ( "Connection closed" ) ,
tr ( "The connection was closed by remote host" )
2018-03-07 18:06:52 +00:00
) . open ( ) ;
2018-11-03 23:39:29 +00:00
sound . play ( Sound . CONNECTION_DISCONNECTED ) ;
2019-02-17 15:08:10 +00:00
auto_reconnect = true ;
2018-03-07 18:06:52 +00:00
break ;
case DisconnectReason . CONNECTION_PING_TIMEOUT :
2018-12-05 19:46:33 +00:00
console . error ( tr ( "Connection ping timeout" ) ) ;
2018-11-03 23:39:29 +00:00
sound . play ( Sound . CONNECTION_DISCONNECTED_TIMEOUT ) ;
2018-03-07 18:06:52 +00:00
createErrorModal (
2018-12-05 19:46:33 +00:00
tr ( "Connection lost" ) ,
tr ( "Lost connection to remote host (Ping timeout)<br>Even possible?" )
2018-03-07 18:06:52 +00:00
) . open ( ) ;
2019-02-17 15:08:10 +00:00
2018-03-07 18:06:52 +00:00
break ;
case DisconnectReason . SERVER_CLOSED :
2018-12-05 19:46:33 +00:00
chat . serverChat ( ) . appendError ( tr ( "Server closed ({0})" ) , data . reasonmsg ) ;
2018-03-07 18:06:52 +00:00
createErrorModal (
2018-12-05 19:46:33 +00:00
tr ( "Server closed" ) ,
"The server is closed.<br>" + //TODO tr
2018-03-07 18:06:52 +00:00
"Reason: " + data . reasonmsg
) . open ( ) ;
2018-11-03 23:39:29 +00:00
sound . play ( Sound . CONNECTION_DISCONNECTED ) ;
2019-02-17 15:08:10 +00:00
auto_reconnect = true ;
2018-03-07 18:06:52 +00:00
break ;
2018-09-24 19:14:36 +00:00
case DisconnectReason . SERVER_REQUIRES_PASSWORD :
2018-12-05 19:46:33 +00:00
chat . serverChat ( ) . appendError ( tr ( "Server requires password" ) ) ;
createInputModal ( tr ( "Server password" ) , tr ( "Enter server password:" ) , password = > password . length != 0 , password = > {
2018-09-24 19:14:36 +00:00
if ( ! ( typeof password === "string" ) ) return ;
this . startConnection ( this . serverConnection . _remote_address . host + ":" + this . serverConnection . _remote_address . port ,
2018-12-28 14:39:23 +00:00
this . serverConnection . _handshakeHandler . profile ,
2018-09-24 19:14:36 +00:00
this . serverConnection . _handshakeHandler . name ,
{ password : password as string , hashed : false } ) ;
} ) . open ( ) ;
2018-11-03 23:39:29 +00:00
break ;
case DisconnectReason . CLIENT_KICKED :
2018-12-05 19:46:33 +00:00
chat . serverChat ( ) . appendError ( tr ( "You got kicked from the server by {0}{1}" ) ,
2018-11-03 23:39:29 +00:00
ClientEntry . chatTag ( data [ "invokerid" ] , data [ "invokername" ] , data [ "invokeruid" ] ) ,
data [ "reasonmsg" ] ? " (" + data [ "reasonmsg" ] + ")" : "" ) ;
sound . play ( Sound . SERVER_KICKED ) ;
2019-02-17 15:08:10 +00:00
auto_reconnect = true ;
2018-11-03 23:39:29 +00:00
break ;
case DisconnectReason . CLIENT_BANNED :
2018-12-05 19:46:33 +00:00
chat . serverChat ( ) . appendError ( tr ( "You got banned from the server by {0}{1}" ) ,
2018-11-03 23:39:29 +00:00
ClientEntry . chatTag ( data [ "invokerid" ] , data [ "invokername" ] , data [ "invokeruid" ] ) ,
data [ "reasonmsg" ] ? " (" + data [ "reasonmsg" ] + ")" : "" ) ;
sound . play ( Sound . CONNECTION_BANNED ) ; //TODO findout if it was a disconnect or a connect refuse
break ;
2018-03-07 18:06:52 +00:00
default :
2018-12-05 19:46:33 +00:00
console . error ( tr ( "Got uncaught disconnect!" ) ) ;
console . error ( tr ( "Type: %o Data:" ) , type ) ;
2018-03-07 18:06:52 +00:00
console . error ( data ) ;
break ;
}
2018-02-27 16:20:49 +00:00
2018-03-07 18:06:52 +00:00
this . channelTree . reset ( ) ;
2019-03-07 14:30:53 +00:00
if ( this . voiceConnection )
this . voiceConnection . dropSession ( ) ;
2018-03-07 19:14:36 +00:00
if ( this . serverConnection ) this . serverConnection . disconnect ( ) ;
2018-09-25 10:57:47 +00:00
this . controlBar . update_connection_state ( ) ;
2018-11-25 12:57:48 +00:00
this . selectInfo . setCurrentSelected ( null ) ;
this . selectInfo . update_banner ( ) ;
2019-02-17 15:08:10 +00:00
if ( auto_reconnect ) {
if ( ! this . serverConnection ) {
console . log ( tr ( "Allowed to auto reconnect but cant reconnect because we dont have any information left..." ) ) ;
return ;
}
chat . serverChat ( ) . appendMessage ( tr ( "Reconnecting in 5 seconds" ) ) ;
console . log ( tr ( "Allowed to auto reconnect. Reconnecting in 5000ms" ) ) ;
const server_address = this . serverConnection . _remote_address ;
const profile = this . serverConnection . _handshakeHandler . profile ;
const name = this . serverConnection . _handshakeHandler . name ;
const password = this . serverConnection . _handshakeHandler . server_password ;
this . _reconnect_timer = setTimeout ( ( ) = > {
this . _reconnect_timer = undefined ;
chat . serverChat ( ) . appendMessage ( tr ( "Reconnecting..." ) ) ;
2019-03-17 11:15:39 +00:00
log . info ( LogCategory . NETWORKING , tr ( "Reconnecting..." ) )
2019-02-17 15:08:10 +00:00
this . startConnection ( server_address . host + ":" + server_address . port , profile , name , password ? { password : password , hashed : true } : undefined ) ;
this . _reconnect_attempt = true ;
} , 5000 ) ;
}
}
cancel_reconnect() {
if ( this . _reconnect_timer ) {
chat . serverChat ( ) . appendMessage ( tr ( "Reconnect canceled" ) ) ;
clearTimeout ( this . _reconnect_timer ) ;
this . _reconnect_timer = undefined ;
}
2018-02-27 16:20:49 +00:00
}
}