221 lines
6.9 KiB
TypeScript
221 lines
6.9 KiB
TypeScript
|
|
import { IStore } from '../app/types';
|
|
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
|
|
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
|
|
import { getCurrentConference } from '../base/conference/functions';
|
|
import { openDialog } from '../base/dialog/actions';
|
|
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
|
|
import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../base/participants/actionTypes';
|
|
import { participantUpdated } from '../base/participants/actions';
|
|
import {
|
|
getLocalParticipant,
|
|
getParticipantById,
|
|
isScreenShareParticipant
|
|
} from '../base/participants/functions';
|
|
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
|
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
|
import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
|
|
|
|
import { PARTICIPANT_VERIFIED, SET_MEDIA_ENCRYPTION_KEY, START_VERIFICATION, TOGGLE_E2EE } from './actionTypes';
|
|
import { setE2EEMaxMode, toggleE2EE } from './actions';
|
|
import ParticipantVerificationDialog from './components/ParticipantVerificationDialog';
|
|
import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID, MAX_MODE } from './constants';
|
|
import { isMaxModeReached, isMaxModeThresholdReached } from './functions';
|
|
import logger from './logger';
|
|
import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
|
|
|
|
|
|
/**
|
|
* Middleware that captures actions related to E2EE.
|
|
*
|
|
* @param {Store} store - The redux store.
|
|
* @returns {Function}
|
|
*/
|
|
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
|
const conference = getCurrentConference(getState);
|
|
|
|
switch (action.type) {
|
|
case APP_WILL_MOUNT:
|
|
dispatch(registerSound(
|
|
E2EE_OFF_SOUND_ID,
|
|
E2EE_OFF_SOUND_FILE));
|
|
|
|
dispatch(registerSound(
|
|
E2EE_ON_SOUND_ID,
|
|
E2EE_ON_SOUND_FILE));
|
|
break;
|
|
|
|
case APP_WILL_UNMOUNT:
|
|
dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
|
|
dispatch(unregisterSound(E2EE_ON_SOUND_ID));
|
|
break;
|
|
|
|
case CONFERENCE_JOINED:
|
|
_updateMaxMode(dispatch, getState);
|
|
|
|
break;
|
|
|
|
case PARTICIPANT_JOINED: {
|
|
const result = next(action);
|
|
|
|
if (!isScreenShareParticipant(action.participant) && !action.participant.local) {
|
|
_updateMaxMode(dispatch, getState);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
case PARTICIPANT_LEFT: {
|
|
const participant = getParticipantById(getState(), action.participant?.id);
|
|
const result = next(action);
|
|
|
|
if (!isScreenShareParticipant(participant)) {
|
|
_updateMaxMode(dispatch, getState);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
case TOGGLE_E2EE: {
|
|
if (conference?.isE2EESupported() && conference.isE2EEEnabled() !== action.enabled) {
|
|
logger.debug(`E2EE will be ${action.enabled ? 'enabled' : 'disabled'}`);
|
|
conference.toggleE2EE(action.enabled);
|
|
|
|
// Broadcast that we enabled / disabled E2EE.
|
|
const participant = getLocalParticipant(getState);
|
|
|
|
dispatch(participantUpdated({
|
|
e2eeEnabled: action.enabled,
|
|
id: participant?.id ?? '',
|
|
local: true
|
|
}));
|
|
|
|
const soundID = action.enabled ? E2EE_ON_SOUND_ID : E2EE_OFF_SOUND_ID;
|
|
|
|
dispatch(playSound(soundID));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SET_MEDIA_ENCRYPTION_KEY: {
|
|
if (conference?.isE2EESupported()) {
|
|
const { exportedKey, index } = action.keyInfo;
|
|
|
|
if (exportedKey) {
|
|
window.crypto.subtle.importKey(
|
|
'raw',
|
|
new Uint8Array(exportedKey),
|
|
'AES-GCM',
|
|
false,
|
|
[ 'encrypt', 'decrypt' ])
|
|
.then(
|
|
encryptionKey => {
|
|
conference.setMediaEncryptionKey({
|
|
encryptionKey,
|
|
index
|
|
});
|
|
})
|
|
.catch(error => logger.error('SET_MEDIA_ENCRYPTION_KEY error', error));
|
|
} else {
|
|
conference.setMediaEncryptionKey({
|
|
encryptionKey: false,
|
|
index
|
|
});
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PARTICIPANT_VERIFIED: {
|
|
const { isVerified, pId } = action;
|
|
|
|
conference?.markParticipantVerified(pId, isVerified);
|
|
break;
|
|
}
|
|
|
|
case START_VERIFICATION: {
|
|
conference?.startVerification(action.pId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return next(action);
|
|
});
|
|
|
|
/**
|
|
* Set up state change listener to perform maintenance tasks when the conference
|
|
* is left or failed.
|
|
*/
|
|
StateListenerRegistry.register(
|
|
state => getCurrentConference(state),
|
|
(conference, { dispatch }, previousConference) => {
|
|
if (previousConference) {
|
|
dispatch(toggleE2EE(false));
|
|
}
|
|
|
|
if (conference) {
|
|
conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_AVAILABLE, (pId: string) => {
|
|
dispatch(participantUpdated({
|
|
e2eeVerificationAvailable: true,
|
|
id: pId
|
|
}));
|
|
});
|
|
|
|
conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_READY, (pId: string, sas: object) => {
|
|
dispatch(openDialog(ParticipantVerificationDialog, { pId,
|
|
sas }));
|
|
});
|
|
|
|
conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_COMPLETED,
|
|
(pId: string, success: boolean, message: string) => {
|
|
if (message) {
|
|
logger.warn('E2EE_VERIFICATION_COMPLETED warning', message);
|
|
}
|
|
dispatch(participantUpdated({
|
|
e2eeVerified: success,
|
|
id: pId
|
|
}));
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Sets the maxMode based on the number of participants in the conference.
|
|
*
|
|
* @param { Dispatch<any>} dispatch - The redux dispatch function.
|
|
* @param {Function|Object} getState - The {@code getState} function.
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
function _updateMaxMode(dispatch: IStore['dispatch'], getState: IStore['getState']) {
|
|
const state = getState();
|
|
|
|
const { e2ee = {} } = state['features/base/config'];
|
|
|
|
if (e2ee.externallyManagedKey) {
|
|
return;
|
|
}
|
|
|
|
const { maxMode, enabled } = state['features/e2ee'];
|
|
const isMaxModeThresholdReachedValue = isMaxModeThresholdReached(state);
|
|
let newMaxMode: string;
|
|
|
|
if (isMaxModeThresholdReachedValue) {
|
|
newMaxMode = MAX_MODE.THRESHOLD_EXCEEDED;
|
|
} else if (isMaxModeReached(state)) {
|
|
newMaxMode = MAX_MODE.ENABLED;
|
|
} else {
|
|
newMaxMode = MAX_MODE.DISABLED;
|
|
}
|
|
|
|
if (maxMode !== newMaxMode) {
|
|
dispatch(setE2EEMaxMode(newMaxMode));
|
|
}
|
|
|
|
if (isMaxModeThresholdReachedValue && !enabled) {
|
|
dispatch(toggleE2EE(false));
|
|
}
|
|
}
|