import { ipcRenderer , webFrame } from 'electron' ; import { buildNumber , name , searchAPIVersion , version , } from '../../package.json' ; import { IDownloadItem } from '../app/download-handler' ; import { apiCmds , apiName , IBadgeCount , IBoundsChange , ICPUUsage , ILogMsg , IMediaPermission , INotificationData , IRestartFloaterData , IScreenSharingIndicator , IScreenSharingIndicatorOptions , IScreenSnippet , IVersionInfo , KeyCodes , LogLevel , NotificationActionCallback , } from '../common/api-interface' ; import { i18n , LocaleType } from '../common/i18n-preload' ; import { throttle } from '../common/utils' ; import { getSource } from './desktop-capturer' ; import SSFNotificationHandler from './notification-ssf-handler' ; import { ScreenSnippetBcHandler } from './screen-snippet-bc-handler' ; const SUPPORTED_SETTINGS = [ 'flashing-notifications' ] ; const MAIN_WINDOW_NAME = 'main' ; let isAltKey = false ; let isMenuOpen = false ; interface ICryptoLib { AESGCMEncrypt : ( name : string , base64IV : string , base64AAD : string , base64Key : string , base64In : string ) => string | null ; AESGCMDecrypt : ( base64IV : string , base64AAD : string , base64Key : string , base64In : string ) => string | null ; } export interface ILocalObject { ipcRenderer ; logger ? : ( msg : ILogMsg , logLevel : LogLevel , showInConsole : boolean ) => void ; activityDetectionCallback ? : ( arg : number ) => void ; downloadManagerCallback ? : ( arg ? : any ) => void ; screenSnippetCallback ? : ( arg : IScreenSnippet ) => void ; boundsChangeCallback ? : ( arg : IBoundsChange ) => void ; screenSharingIndicatorCallback ? : ( arg : IScreenSharingIndicator ) => void ; protocolActionCallback ? : ( arg : string ) => void ; collectLogsCallback ? : Array < ( ) => void > ; analyticsEventHandler ? : ( arg : any ) => void ; restartFloater ? : ( arg : IRestartFloaterData ) => void ; } const local = { ipcRenderer , } ; const notificationActionCallbacks = new Map < number , NotificationActionCallback > ( ) ; const DEFAULT_THROTTLE = 1000 ; const throttledSetBadgeCount = throttle ( ( count ) => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . setBadgeCount , count , } ) ; } , DEFAULT_THROTTLE ) ; const throttledSetLocale = throttle ( ( locale ) => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . setLocale , locale , } ) ; } , DEFAULT_THROTTLE ) ; const throttledActivate = throttle ( ( windowName ) => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . activate , windowName , } ) ; } , DEFAULT_THROTTLE ) ; const throttledBringToFront = throttle ( ( windowName , reason ) => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . bringToFront , windowName , reason , } ) ; } , DEFAULT_THROTTLE ) ; const throttledCloseScreenShareIndicator = throttle ( ( streamId ) => { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . closeWindow , windowType : 'screen-sharing-indicator' , winKey : streamId , } ) ; } , DEFAULT_THROTTLE ) ; const throttledSetIsInMeetingStatus = throttle ( ( isInMeeting ) => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . setIsInMeeting , isInMeeting , } ) ; } , DEFAULT_THROTTLE ) ; const throttledSetCloudConfig = throttle ( ( data ) => { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . setCloudConfig , cloudConfig : data , } ) ; } , DEFAULT_THROTTLE ) ; const throttledOpenDownloadedItem = throttle ( ( id ) => { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . openDownloadedItem , id , } ) ; } , DEFAULT_THROTTLE ) ; const throttledShowDownloadedItem = throttle ( ( id ) => { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . showDownloadedItem , id , } ) ; } , DEFAULT_THROTTLE ) ; const throttledClearDownloadedItems = throttle ( ( ) => { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . clearDownloadedItems , } ) ; } , DEFAULT_THROTTLE ) ; const throttledRestart = throttle ( ( ) => { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . restartApp , } ) ; } , DEFAULT_THROTTLE ) ; const throttledSetZoomLevel = throttle ( ( zoomLevel ) => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . setZoomLevel , zoomLevel , } ) ; } , DEFAULT_THROTTLE ) ; let cryptoLib ; try { } catch ( e ) { cryptoLib = null ; console . warn ( "Failed to initialize Crypto Lib. You'll need to include the Crypto library. Contact the developers for more details" ) ; } let swiftSearch ; try { } catch ( e ) { swiftSearch = null ; console . warn ( "Failed to initialize swift search. You'll need to include the search dependency. Contact the developers for more details" ) ; } let swiftSearchUtils ; try { } catch ( e ) { swiftSearchUtils = null ; console . warn ( "Failed to initialize swift search utils. You'll need to include the search dependency. Contact the developers for more details" ) ; } let nextIndicatorId = 0 ; export class SSFApi { /**
     * Native encryption and decryption.
     */ public CryptoLib : ICryptoLib | null = cryptoLib ; public Search : any = swiftSearch ; public SearchUtils : any = swiftSearchUtils ; public Notification = SSFNotificationHandler ; /**
     * Implements equivalent of desktopCapturer.getSources - that works in
     * a sandboxed renderer process.
     * see: https://electron.atom.io/docs/api/desktop-capturer/
     * for interface: see documentation in desktopCapturer/getSource.js
     *
     * This opens a window and displays all the desktop sources
     * and returns selected source
     */ public getMediaSource = getSource ; param { String } windowName public activate ( windowName ) { if ( typeof windowName === 'string' ) { throttledActivate ( windowName ) ; } } param { String } windowName param { String } reason public bringToFront ( windowName , reason ) { if ( typeof windowName === 'string' ) { throttledBringToFront ( windowName , reason ) ; } } /**
     * Method that returns various version info
     */ public getVersionInfo ( ) { const appName = name ; const appVer = version ; const cpuArch = process . arch || '' ; return Promise . resolve ( { containerIdentifier : appName , containerVer : appVer , buildNumber , apiVer : '3.0.0' , cpuArch , searchApiVer : searchAPIVersion , } ) ; } param { Object } period param { Object } activityDetectionCallback example public registerActivityDetection ( period , activityDetectionCallback ) { if ( typeof activityDetectionCallback === 'function' ) { local . activityDetectionCallback = activityDetectionCallback ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . registerActivityDetection , period , } ) ; } } param downloadManagerCallback public registerDownloadHandler ( downloadManagerCallback ) { if ( typeof downloadManagerCallback === 'function' ) { local . downloadManagerCallback = downloadManagerCallback ; } local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . registerDownloadHandler , } ) ; } param { Function } callback public registerBoundsChange ( callback ) { if ( typeof callback === 'function' ) { local . boundsChangeCallback = callback ; } } param { Object } logger public registerLogger ( logger ) { if ( typeof logger === 'function' ) { local . logger = logger ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . registerLogger , } ) ; } } param protocolHandler { Function } public registerProtocolHandler ( protocolHandler ) { if ( typeof protocolHandler === 'function' ) { local . protocolActionCallback = protocolHandler ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . registerProtocolHandler , } ) ; } } /**
     * Allows JS to register a log retriever that can be used by the
     * electron main process to retrieve current logs.
     */ public registerLogRetriever ( collectLogs , logName ) { if ( typeof collectLogs === 'function' ) { if ( ! local . collectLogsCallback ) { local . collectLogsCallback = new Array < ( ) => void > ( ) ; } local . collectLogsCallback . push ( collectLogs ) ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . registerLogRetriever , logName , } ) ; } } /**
     * Send log files to main process when requested.
     */ public sendLogs ( logName , logFiles ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . sendLogs , logs : { logName , logFiles } , } ) ; } param analyticsEventHandler public registerAnalyticsEvent ( analyticsEventHandler ) { if ( typeof analyticsEventHandler === 'function' ) { local . analyticsEventHandler = analyticsEventHandler ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . registerAnalyticsHandler , } ) ; } } deprecated public ScreenSnippet = ScreenSnippetBcHandler ; param screenSnippetCallback { function } public openScreenSnippet ( screenSnippetCallback ) { if ( typeof screenSnippetCallback === 'function' ) { local . screenSnippetCallback = screenSnippetCallback ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . openScreenSnippet , } ) ; } } /**
     * Cancel a screen capture in progress
     */ public closeScreenSnippet ( ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . closeScreenSnippet , } ) ; } /**
     * Auto update
     */ public autoUpdate ( filename ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . autoUpdate , filename , } ) ; } param { number } count public setBadgeCount ( count ) { throttledSetBadgeCount ( count ) ; } param { string } locale example public setLocale ( locale ) { if ( typeof locale === 'string' ) { i18n . setLocale ( locale as LocaleType ) ; throttledSetLocale ( locale ) ; } } /**
     * Sets if the user is in an active meeting
     * will be used to handle memory refresh functionality
     */ public setIsInMeeting ( isInMeeting ) { throttledSetIsInMeetingStatus ( isInMeeting ) ; } /**
     * Opens a modal window to configure notification preference.
     */ public showNotificationSettings ( data ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . showNotificationSettings , windowName : MAIN_WINDOW_NAME , theme : data , } ) ; } param options param callback public showScreenSharingIndicator ( options , callback ) { const { displayId , stream } = options ; if ( ! stream || ! stream . active || stream . getVideoTracks ( ) . length !== 1 ) { callback ( { type : 'error' , reason : 'bad stream' } ) ; return ; } if ( displayId && typeof displayId !== 'string' ) { callback ( { type : 'error' , reason : 'bad displayId' } ) ; return ; } const destroy = ( ) => { throttledCloseScreenShareIndicator ( stream . id ) ; stream . removeEventListener ( 'inactive' , destroy ) ; } ; stream . addEventListener ( 'inactive' , destroy ) ; if ( typeof callback === 'function' ) { local . screenSharingIndicatorCallback = callback ; ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . openScreenSharingIndicator , displayId , id : ++ nextIndicatorId , streamId : stream . id , } ) ; } } param options param callback public openScreenSharingIndicator ( options , callback ) { const { displayId , requestId , streamId } = options ; if ( typeof callback === 'function' ) { local . screenSharingIndicatorCallback = callback ; ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . openScreenSharingIndicator , displayId , id : requestId , streamId , } ) ; } } /**
     * Closes the screen sharing indicator
     */ public closeScreenSharingIndicator ( winKey ) { throttledCloseScreenShareIndicator ( winKey ) ; } param callback public registerRestartFloater ( callback ) { local . restartFloater = callback ; } param data { ICloudConfig } public setCloudConfig ( data ) { throttledSetCloudConfig ( data ) ; } param id public openDownloadedItem ( id ) { throttledOpenDownloadedItem ( id ) ; } param id public showDownloadedItem ( id ) { throttledShowDownloadedItem ( id ) ; } /**
     * Clears downloaded items
     */ public clearDownloadedItems ( ) { throttledClearDownloadedItems ( ) ; } /**
     * Restart the app
     */ public restartApp ( ) { throttledRestart ( ) ; } /**
     * get CPU usage
     */ public async getCPUUsage ( ) { return Promise . resolve ( await process . getCPUUsage ( ) ) ; } /**
     * Check media permission
     */ public async checkMediaPermission ( ) { const mediaStatus = ( await ipcRenderer . invoke ( apiName . symphonyApi , { cmd : apiCmds . getMediaAccessStatus , } ) ) as IMediaPermission ; return Promise . resolve ( { camera : mediaStatus . camera , microphone : mediaStatus . microphone , screen : mediaStatus . screen , } ) ; } param isMana public setIsMana ( isMana ) { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . setIsMana , isMana , } ) ; } /**
     * Closes all browser windows on SDA side, such as notifications, Screen snippet window, popped out chats etc
     */ public closeAllWrapperWindows ( ) { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . closeAllWrapperWindows , } ) ; } param notificationOpts { INotificationData } param notificationCallback { NotificationActionCallback } public showNotification ( notificationOpts , notificationCallback ) { if ( typeof notificationOpts . id === 'number' ) { notificationActionCallbacks . set ( notificationOpts . id , notificationCallback ) ; } if ( notificationOpts . callback ) { delete notificationOpts . callback ; } ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . showNotification , notificationOpts , } ) ; } param notificationId { number } public closeNotification ( notificationId ) { ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . closeNotification , notificationId , } ) ; } /**
     * Get zoom level
     *
     */ public getZoomLevel ( ) { return new Promise ( ( resolve ) => { resolve ( webFrame . getZoomFactor ( ) ) ; } ) ; } param { string } zoomLevel example public setZoomLevel ( zoomLevel ) { if ( typeof zoomLevel === 'number' ) { throttledSetZoomLevel ( zoomLevel ) ; } } returns public supportedSettings ( ) { return SUPPORTED_SETTINGS || [ ] ; } } /**
 * Ipc events
 */ param { IBadgeCount } arg local . ipcRenderer . on ( 'create-badge-data-url' , ( _event , arg ) => { const count = ( arg && arg . count ) || 0 ; const radius = 16 ; const canvas = document . createElement ( 'canvas' ) ; canvas . height = radius * 2 ; canvas . width = radius * 2 ; const ctx = canvas . getContext ( '2d' ) ; if ( ctx ) { ctx . fillStyle = 'red' ; ctx . beginPath ( ) ; ctx . arc ( radius , radius , radius , 0 , 2 * Math . PI , false ) ; ctx . fill ( ) ; ctx . textAlign = 'center' ; ctx . fillStyle = 'white' ; const text = count > 99 ? '99+' : count . toString ( ) ; if ( text . length > 2 ) { ctx . font = 'bold 18px sans-serif' ; ctx . fillText ( text , radius , 22 ) ; } else if ( text . length > 1 ) { ctx . font = 'bold 24px sans-serif' ; ctx . fillText ( text , radius , 24 ) ; } else { ctx . font = 'bold 26px sans-serif' ; ctx . fillText ( text , radius , 26 ) ; } const dataUrl = canvas . toDataURL ( 'image/png' , 1.0 ) ; local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . badgeDataUrl , count , dataUrl , } ) ; } } ) ; param { IScreenSnippet } arg local . ipcRenderer . on ( 'screen-snippet-data' , ( _event , arg ) => { if ( typeof arg === 'object' && typeof local . screenSnippetCallback === 'function' ) { local . screenSnippetCallback ( arg ) ; } } ) ; local . ipcRenderer . on ( 'collect-logs' , ( _event ) => { if ( local . collectLogsCallback ) { for ( const callback of local . collectLogsCallback ) { callback ( ) ; } } } ) ; param { number } idleTime local . ipcRenderer . on ( 'activity' , ( _event , idleTime ) => { if ( typeof idleTime === 'number' && typeof local . activityDetectionCallback === 'function' ) { local . activityDetectionCallback ( idleTime ) ; } } ) ; local . ipcRenderer . on ( 'download-completed' , ( _event , downloadItem ) => { if ( typeof downloadItem === 'object' && typeof local . downloadManagerCallback === 'function' ) { local . downloadManagerCallback ( { status : 'download-completed' , item : downloadItem , } ) ; } } ) ; local . ipcRenderer . on ( 'download-failed' , ( _event ) => { if ( typeof local . downloadManagerCallback === 'function' ) { local . downloadManagerCallback ( { status : 'download-failed' } ) ; } } ) ; param { IBoundsChange } arg local . ipcRenderer . on ( 'boundsChange' , ( _event , arg ) : void => { const { x , y , height , width , windowName } = arg ; if ( x && y && height && width && windowName && typeof local . boundsChangeCallback === 'function' ) { local . boundsChangeCallback ( { x , y , height , width , windowName , } ) ; } } ) ; /**
 * An event triggered by the main process
 * when the screen sharing has been stopper
 */ local . ipcRenderer . on ( 'screen-sharing-stopped' , ( _event , id ) => { if ( typeof local . screenSharingIndicatorCallback === 'function' ) { local . screenSharingIndicatorCallback ( { type : 'stopRequested' , requestId : id , } ) ; } } ) ; param { object } arg local . ipcRenderer . on ( 'log' , ( _event , arg ) => { if ( arg && local . logger ) { local . logger ( arg . msgs || [ ] , arg . logLevel , arg . showInConsole ) ; } } ) ; param { String } arg local . ipcRenderer . on ( 'protocol-action' , ( _event , arg ) => { if ( typeof local . protocolActionCallback === 'function' && typeof arg === 'string' ) { local . protocolActionCallback ( arg ) ; } } ) ; local . ipcRenderer . on ( 'analytics-callback' , ( _event , arg ) => { if ( typeof local . analyticsEventHandler === 'function' && arg ) { local . analyticsEventHandler ( arg ) ; } } ) ; param { IRestartFloaterData } local . ipcRenderer . on ( 'restart-floater' , ( _event , { windowName , bounds } ) => { if ( typeof local . restartFloater === 'function' && windowName ) { local . restartFloater ( { windowName , bounds } ) ; } } ) ; param { INotificationData } local . ipcRenderer . on ( 'notification-actions' , ( _event , args ) => { const callback = notificationActionCallbacks . get ( args . data . id ) ; const data = args . data ; data . notificationData = args . notificationData ; if ( args && callback ) { callback ( args . event , data ) ; } } ) ; const sanitize = ( ) : void => { if ( window . name === apiName . mainWindowName ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . sanitize , windowName : window . name , } ) ; } } ; const updateOnlineStatus = ( ) : void => { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . isOnline , isOnline : window . navigator . onLine , } ) ; } ; const throttledKeyDown = throttle ( ( event ) => { isAltKey = event . keyCode === KeyCodes . Alt ; if ( event . keyCode === KeyCodes . Esc ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . keyPress , keyCode : event . keyCode , } ) ; } } , 500 ) ; const throttledKeyUp = throttle ( ( event ) => { if ( isAltKey && ( event . keyCode === KeyCodes . Alt || KeyCodes . Esc ) ) { isMenuOpen = ! isMenuOpen ; } if ( isAltKey && isMenuOpen && event . keyCode === KeyCodes . Alt ) { local . ipcRenderer . send ( apiName . symphonyApi , { cmd : apiCmds . keyPress , keyCode : event . keyCode , } ) ; } } , 500 ) ; const throttleMouseDown = throttle ( ( ) => { if ( isAltKey && isMenuOpen ) { isMenuOpen = ! isMenuOpen ; } } , 500 ) ; /**
 * Window Events
 */ window . addEventListener ( 'beforeunload' , sanitize , false ) ; window . addEventListener ( 'offline' , updateOnlineStatus , false ) ; window . addEventListener ( 'online' , updateOnlineStatus , false ) ; window . addEventListener ( 'keyup' , throttledKeyUp , true ) ; window . addEventListener ( 'keydown' , throttledKeyDown , true ) ; window . addEventListener ( 'mousedown' , throttleMouseDown , { capture : true } ) ;