您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

middleware.ts 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import { IStore } from '../app/types';
  2. import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
  3. import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
  4. import { getCurrentConference } from '../base/conference/functions';
  5. import { openDialog } from '../base/dialog/actions';
  6. import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
  7. import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../base/participants/actionTypes';
  8. import { participantUpdated } from '../base/participants/actions';
  9. import {
  10. getLocalParticipant,
  11. getParticipantById,
  12. isScreenShareParticipant
  13. } from '../base/participants/functions';
  14. import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
  15. import StateListenerRegistry from '../base/redux/StateListenerRegistry';
  16. import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
  17. import { PARTICIPANT_VERIFIED, SET_MEDIA_ENCRYPTION_KEY, START_VERIFICATION, TOGGLE_E2EE } from './actionTypes';
  18. import { setE2EEMaxMode, toggleE2EE } from './actions';
  19. import ParticipantVerificationDialog from './components/ParticipantVerificationDialog';
  20. import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID, MAX_MODE } from './constants';
  21. import { isMaxModeReached, isMaxModeThresholdReached } from './functions';
  22. import logger from './logger';
  23. import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
  24. /**
  25. * Middleware that captures actions related to E2EE.
  26. *
  27. * @param {Store} store - The redux store.
  28. * @returns {Function}
  29. */
  30. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  31. const conference = getCurrentConference(getState);
  32. switch (action.type) {
  33. case APP_WILL_MOUNT:
  34. dispatch(registerSound(
  35. E2EE_OFF_SOUND_ID,
  36. E2EE_OFF_SOUND_FILE));
  37. dispatch(registerSound(
  38. E2EE_ON_SOUND_ID,
  39. E2EE_ON_SOUND_FILE));
  40. break;
  41. case APP_WILL_UNMOUNT:
  42. dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
  43. dispatch(unregisterSound(E2EE_ON_SOUND_ID));
  44. break;
  45. case CONFERENCE_JOINED:
  46. _updateMaxMode(dispatch, getState);
  47. break;
  48. case PARTICIPANT_JOINED: {
  49. const result = next(action);
  50. if (!isScreenShareParticipant(action.participant) && !action.participant.local) {
  51. _updateMaxMode(dispatch, getState);
  52. }
  53. return result;
  54. }
  55. case PARTICIPANT_LEFT: {
  56. const participant = getParticipantById(getState(), action.participant?.id);
  57. const result = next(action);
  58. if (!isScreenShareParticipant(participant)) {
  59. _updateMaxMode(dispatch, getState);
  60. }
  61. return result;
  62. }
  63. case TOGGLE_E2EE: {
  64. if (conference?.isE2EESupported() && conference.isE2EEEnabled() !== action.enabled) {
  65. logger.debug(`E2EE will be ${action.enabled ? 'enabled' : 'disabled'}`);
  66. conference.toggleE2EE(action.enabled);
  67. // Broadcast that we enabled / disabled E2EE.
  68. const participant = getLocalParticipant(getState);
  69. dispatch(participantUpdated({
  70. e2eeEnabled: action.enabled,
  71. id: participant?.id ?? '',
  72. local: true
  73. }));
  74. const soundID = action.enabled ? E2EE_ON_SOUND_ID : E2EE_OFF_SOUND_ID;
  75. dispatch(playSound(soundID));
  76. }
  77. break;
  78. }
  79. case SET_MEDIA_ENCRYPTION_KEY: {
  80. if (conference?.isE2EESupported()) {
  81. const { exportedKey, index } = action.keyInfo;
  82. if (exportedKey) {
  83. window.crypto.subtle.importKey(
  84. 'raw',
  85. new Uint8Array(exportedKey),
  86. 'AES-GCM',
  87. false,
  88. [ 'encrypt', 'decrypt' ])
  89. .then(
  90. encryptionKey => {
  91. conference.setMediaEncryptionKey({
  92. encryptionKey,
  93. index
  94. });
  95. })
  96. .catch(error => logger.error('SET_MEDIA_ENCRYPTION_KEY error', error));
  97. } else {
  98. conference.setMediaEncryptionKey({
  99. encryptionKey: false,
  100. index
  101. });
  102. }
  103. }
  104. break;
  105. }
  106. case PARTICIPANT_VERIFIED: {
  107. const { isVerified, pId } = action;
  108. conference?.markParticipantVerified(pId, isVerified);
  109. break;
  110. }
  111. case START_VERIFICATION: {
  112. conference?.startVerification(action.pId);
  113. break;
  114. }
  115. }
  116. return next(action);
  117. });
  118. /**
  119. * Set up state change listener to perform maintenance tasks when the conference
  120. * is left or failed.
  121. */
  122. StateListenerRegistry.register(
  123. state => getCurrentConference(state),
  124. (conference, { dispatch }, previousConference) => {
  125. if (previousConference) {
  126. dispatch(toggleE2EE(false));
  127. }
  128. if (conference) {
  129. conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_AVAILABLE, (pId: string) => {
  130. dispatch(participantUpdated({
  131. e2eeVerificationAvailable: true,
  132. id: pId
  133. }));
  134. });
  135. conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_READY, (pId: string, sas: object) => {
  136. dispatch(openDialog(ParticipantVerificationDialog, { pId,
  137. sas }));
  138. });
  139. conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_COMPLETED,
  140. (pId: string, success: boolean, message: string) => {
  141. if (message) {
  142. logger.warn('E2EE_VERIFICATION_COMPLETED warning', message);
  143. }
  144. dispatch(participantUpdated({
  145. e2eeVerified: success,
  146. id: pId
  147. }));
  148. });
  149. }
  150. });
  151. /**
  152. * Sets the maxMode based on the number of participants in the conference.
  153. *
  154. * @param { Dispatch<any>} dispatch - The redux dispatch function.
  155. * @param {Function|Object} getState - The {@code getState} function.
  156. * @private
  157. * @returns {void}
  158. */
  159. function _updateMaxMode(dispatch: IStore['dispatch'], getState: IStore['getState']) {
  160. const state = getState();
  161. const { e2ee = {} } = state['features/base/config'];
  162. if (e2ee.externallyManagedKey) {
  163. return;
  164. }
  165. const { maxMode, enabled } = state['features/e2ee'];
  166. const isMaxModeThresholdReachedValue = isMaxModeThresholdReached(state);
  167. let newMaxMode: string;
  168. if (isMaxModeThresholdReachedValue) {
  169. newMaxMode = MAX_MODE.THRESHOLD_EXCEEDED;
  170. } else if (isMaxModeReached(state)) {
  171. newMaxMode = MAX_MODE.ENABLED;
  172. } else {
  173. newMaxMode = MAX_MODE.DISABLED;
  174. }
  175. if (maxMode !== newMaxMode) {
  176. dispatch(setE2EEMaxMode(newMaxMode));
  177. }
  178. if (isMaxModeThresholdReachedValue && !enabled) {
  179. dispatch(toggleE2EE(false));
  180. }
  181. }