2020-09-07 19:07:14 +02:00
import "broadcastchannel-polyfill" ;
2021-02-20 17:46:17 +01:00
import { LogCategory , logDebug , logError , logTrace , logWarn } from "../log" ;
2020-09-12 15:49:20 +02:00
import { ConnectHandler } from "../ipc/ConnectHandler" ;
2021-01-10 17:36:57 +01:00
import { tr } from "tc-shared/i18n/localize" ;
2021-02-20 17:46:17 +01:00
import { guid } from "tc-shared/crypto/uid" ;
import { AppParameters } from "tc-shared/settings" ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
interface IpcRawMessage {
timestampSend : number ,
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
sourcePeerId : string ,
targetPeerId : string ,
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
targetChannelId : string ,
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
message : ChannelMessage
2020-07-20 19:08:13 +02:00
}
export interface ChannelMessage {
2021-02-20 17:46:17 +01:00
type : string ,
data : any
2020-07-20 19:08:13 +02:00
}
export abstract class BasicIPCHandler {
protected static readonly BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000" ;
2021-02-20 17:46:17 +01:00
protected readonly applicationChannelId : string ;
protected readonly localPeerId : string ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
protected registeredChannels : IPCChannel [ ] = [ ] ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
protected constructor ( applicationChannelId : string ) {
this . applicationChannelId = applicationChannelId ;
this . localPeerId = guid ( ) ;
2020-07-20 19:08:13 +02:00
}
2021-02-20 17:46:17 +01:00
setup() { }
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
getApplicationChannelId ( ) : string { return this . applicationChannelId ; }
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
getLocalPeerId ( ) : string { return this . localPeerId ; }
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
abstract sendMessage ( message : IpcRawMessage ) ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
protected handleMessage ( message : IpcRawMessage ) {
logTrace ( LogCategory . IPC , tr ( "Received message %o" ) , message ) ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
if ( message . targetPeerId !== this . localPeerId && message . targetPeerId !== BasicIPCHandler . BROADCAST_UNIQUE_ID ) {
/* The message isn't for us */
return ;
}
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
let channelInvokeCount = 0 ;
for ( const channel of this . registeredChannels ) {
if ( channel . channelId !== message . targetChannelId ) {
continue ;
2020-07-20 19:08:13 +02:00
}
2021-02-20 17:46:17 +01:00
if ( typeof channel . targetPeerId === "string" && channel . targetPeerId !== message . sourcePeerId ) {
continue ;
2020-08-07 13:40:11 +02:00
}
2021-02-20 17:46:17 +01:00
if ( channel . messageHandler ) {
channel . messageHandler ( message . sourcePeerId , message . targetPeerId === BasicIPCHandler . BROADCAST_UNIQUE_ID , message . message ) ;
2020-07-20 19:08:13 +02:00
}
2021-02-20 17:46:17 +01:00
channelInvokeCount ++ ;
}
if ( ! channelInvokeCount ) {
/* Seems like we're not the only web/teaclient instance */
/* console.warn(tr("Received channel message for unknown channel (%s)"), data.channelId); */
2020-07-20 19:08:13 +02:00
}
}
2021-02-20 17:46:17 +01:00
/ * *
* @param channelId
* @param remotePeerId The peer to receive messages from . If empty messages will be broadcasted
* /
createChannel ( channelId : string , remotePeerId? : string ) : IPCChannel {
2020-07-20 19:08:13 +02:00
let channel : IPCChannel = {
2021-02-20 17:46:17 +01:00
channelId : channelId ,
targetPeerId : remotePeerId ,
2020-07-20 19:08:13 +02:00
messageHandler : undefined ,
2021-02-20 17:46:17 +01:00
sendMessage : ( type : string , data : any , remotePeerId? : string ) = > {
if ( typeof remotePeerId !== "undefined" ) {
if ( typeof channel . targetPeerId === "string" && remotePeerId != channel . targetPeerId ) {
2020-07-20 19:08:13 +02:00
throw "target id does not match channel target" ;
2020-12-09 14:54:25 +01:00
}
2020-07-20 19:08:13 +02:00
}
2021-02-20 17:46:17 +01:00
remotePeerId = remotePeerId || channel . targetPeerId || BasicIPCHandler . BROADCAST_UNIQUE_ID ;
this . sendMessage ( {
timestampSend : Date.now ( ) ,
sourcePeerId : this.localPeerId ,
targetPeerId : remotePeerId ,
targetChannelId : channelId ,
message : {
data ,
type ,
}
} ) ;
if ( remotePeerId === this . localPeerId || remotePeerId === BasicIPCHandler . BROADCAST_UNIQUE_ID ) {
for ( const localChannel of this . registeredChannels ) {
if ( localChannel . channelId !== channel . channelId ) {
continue ;
}
if ( typeof localChannel . targetPeerId === "string" && localChannel . targetPeerId !== this . localPeerId ) {
continue ;
}
if ( localChannel === channel ) {
continue ;
}
if ( localChannel . messageHandler ) {
localChannel . messageHandler ( this . localPeerId , remotePeerId === BasicIPCHandler . BROADCAST_UNIQUE_ID , {
type : type ,
data : data ,
} ) ;
}
}
}
2020-07-20 19:08:13 +02:00
}
} ;
2020-12-09 14:54:25 +01:00
this . registeredChannels . push ( channel ) ;
2020-07-20 19:08:13 +02:00
return channel ;
}
2021-02-20 17:46:17 +01:00
/ * *
* Create a channel which only communicates with the TeaSpeak - Core .
* @param channelId
* /
createCoreControlChannel ( channelId : string ) : IPCChannel {
return this . createChannel ( channelId , AppParameters . getValue ( AppParameters . KEY_IPC_CORE_PEER_ADDRESS , this . localPeerId ) ) ;
2020-07-20 19:08:13 +02:00
}
2021-02-20 17:46:17 +01:00
channels ( ) : IPCChannel [ ] { return this . registeredChannels ; }
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
deleteChannel ( channel : IPCChannel ) {
this . registeredChannels . remove ( channel ) ;
2020-07-20 19:08:13 +02:00
}
}
export interface IPCChannel {
2021-02-20 17:46:17 +01:00
/** Channel id */
2020-07-20 19:08:13 +02:00
readonly channelId : string ;
2021-02-20 17:46:17 +01:00
/** Target peer id. If set only messages from that process will be processed */
targetPeerId? : string ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
messageHandler : ( sourcePeerId : string , broadcast : boolean , message : ChannelMessage ) = > void ;
sendMessage ( type : string , data : any , remotePeerId? : string ) ;
2020-07-20 19:08:13 +02:00
}
class BroadcastChannelIPC extends BasicIPCHandler {
private channel : BroadcastChannel ;
2021-02-20 17:46:17 +01:00
constructor ( applicationChannelId : string ) {
super ( applicationChannelId ) ;
2020-07-20 19:08:13 +02:00
}
setup() {
super . setup ( ) ;
2021-02-20 17:46:17 +01:00
this . channel = new BroadcastChannel ( this . applicationChannelId ) ;
2020-07-20 19:08:13 +02:00
this . channel . onmessage = this . onMessage . bind ( this ) ;
this . channel . onmessageerror = this . onError . bind ( this ) ;
}
private onMessage ( event : MessageEvent ) {
if ( typeof ( event . data ) !== "string" ) {
2021-01-10 17:36:57 +01:00
logWarn ( LogCategory . IPC , tr ( "Received message with an invalid type (%s): %o" ) , typeof ( event . data ) , event . data ) ;
2020-07-20 19:08:13 +02:00
return ;
}
2021-02-20 17:46:17 +01:00
let message : IpcRawMessage ;
2020-07-20 19:08:13 +02:00
try {
message = JSON . parse ( event . data ) ;
} catch ( error ) {
2021-01-10 17:36:57 +01:00
logError ( LogCategory . IPC , tr ( "Received an invalid encoded message: %o" ) , event . data ) ;
2020-07-20 19:08:13 +02:00
return ;
}
super . handleMessage ( message ) ;
}
private onError ( event : MessageEvent ) {
2021-01-10 17:36:57 +01:00
logWarn ( LogCategory . IPC , tr ( "Received error: %o" ) , event ) ;
2020-07-20 19:08:13 +02:00
}
2021-02-20 17:46:17 +01:00
sendMessage ( message : IpcRawMessage ) {
this . channel . postMessage ( JSON . stringify ( message ) ) ;
2020-07-20 19:08:13 +02:00
}
}
2021-02-20 17:46:17 +01:00
let handlerInstance : BasicIPCHandler ;
let connectHandler : ConnectHandler ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
export function setupIpcHandler() {
if ( handlerInstance ) {
throw "IPC handler already initialized" ;
}
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
handlerInstance = new BroadcastChannelIPC ( AppParameters . getValue ( AppParameters . KEY_IPC_APP_ADDRESS , guid ( ) ) ) ;
handlerInstance . setup ( ) ;
logDebug ( LogCategory . IPC , tr ( "Application IPC started for %s. Local peer address: %s" ) , handlerInstance . getApplicationChannelId ( ) , handlerInstance . getLocalPeerId ( ) ) ;
2020-07-20 19:08:13 +02:00
2021-02-20 17:46:17 +01:00
connectHandler = new ConnectHandler ( handlerInstance ) ;
connectHandler . setup ( ) ;
2020-07-20 19:08:13 +02:00
}
2020-12-09 14:54:25 +01:00
export function getIpcInstance() {
2021-02-20 17:46:17 +01:00
return handlerInstance ;
2020-07-20 19:08:13 +02:00
}
export function getInstanceConnectHandler() {
2021-02-20 17:46:17 +01:00
return connectHandler ;
2020-07-20 19:08:13 +02:00
}