Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

middleware.ts 6.6KB

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