You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /* global APP, JitsiMeetJS, config */
  2. import { jitsiLocalStorage } from '@jitsi/js-utils';
  3. import Logger from '@jitsi/logger';
  4. import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
  5. import { LoginDialog } from './react/features/authentication/components';
  6. import { isTokenAuthEnabled } from './react/features/authentication/functions';
  7. import {
  8. connectionEstablished,
  9. connectionFailed,
  10. constructOptions
  11. } from './react/features/base/connection/actions.web';
  12. import { openDialog } from './react/features/base/dialog/actions';
  13. import { setJWT } from './react/features/base/jwt';
  14. import {
  15. JitsiConnectionErrors,
  16. JitsiConnectionEvents
  17. } from './react/features/base/lib-jitsi-meet';
  18. import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions';
  19. import { getCustomerDetails } from './react/features/jaas/actions.any';
  20. import { getJaasJWT, isVpaasMeeting } from './react/features/jaas/functions';
  21. import {
  22. setPrejoinDisplayNameRequired
  23. } from './react/features/prejoin/actions';
  24. const logger = Logger.getLogger(__filename);
  25. /**
  26. * The feature announced so we can distinguish jibri participants.
  27. *
  28. * @type {string}
  29. */
  30. export const DISCO_JIBRI_FEATURE = 'http://jitsi.org/protocol/jibri';
  31. /**
  32. * Checks if we have data to use attach instead of connect. If we have the data
  33. * executes attach otherwise check if we have to wait for the data. If we have
  34. * to wait for the attach data we are setting handler to APP.connect.handler
  35. * which is going to be called when the attach data is received otherwise
  36. * executes connect.
  37. *
  38. * @param {string} [id] user id
  39. * @param {string} [password] password
  40. * @param {string} [roomName] the name of the conference.
  41. */
  42. function checkForAttachParametersAndConnect(id, password, connection) {
  43. if (window.XMPPAttachInfo) {
  44. APP.connect.status = 'connecting';
  45. // When connection optimization is not deployed or enabled the default
  46. // value will be window.XMPPAttachInfo.status = "error"
  47. // If the connection optimization is deployed and enabled and there is
  48. // a failure the value will be window.XMPPAttachInfo.status = "error"
  49. if (window.XMPPAttachInfo.status === 'error') {
  50. connection.connect({
  51. id,
  52. password
  53. });
  54. return;
  55. }
  56. const attachOptions = window.XMPPAttachInfo.data;
  57. if (attachOptions) {
  58. connection.attach(attachOptions);
  59. delete window.XMPPAttachInfo.data;
  60. } else {
  61. connection.connect({
  62. id,
  63. password
  64. });
  65. }
  66. } else {
  67. APP.connect.status = 'ready';
  68. APP.connect.handler
  69. = checkForAttachParametersAndConnect.bind(
  70. null,
  71. id, password, connection);
  72. }
  73. }
  74. /**
  75. * Try to open connection using provided credentials.
  76. * @param {string} [id]
  77. * @param {string} [password]
  78. * @returns {Promise<JitsiConnection>} connection if
  79. * everything is ok, else error.
  80. */
  81. export async function connect(id, password) {
  82. const state = APP.store.getState();
  83. let { jwt } = state['features/base/jwt'];
  84. const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
  85. if (!iAmRecorder && !iAmSipGateway && isVpaasMeeting(state)) {
  86. await APP.store.dispatch(getCustomerDetails());
  87. if (!jwt) {
  88. jwt = await getJaasJWT(state);
  89. APP.store.dispatch(setJWT(jwt));
  90. }
  91. }
  92. const connection = new JitsiMeetJS.JitsiConnection(null, jwt, constructOptions(state));
  93. if (config.iAmRecorder) {
  94. connection.addFeature(DISCO_JIBRI_FEATURE);
  95. }
  96. return new Promise((resolve, reject) => {
  97. connection.addEventListener(
  98. JitsiConnectionEvents.CONNECTION_ESTABLISHED,
  99. handleConnectionEstablished);
  100. connection.addEventListener(
  101. JitsiConnectionEvents.CONNECTION_FAILED,
  102. handleConnectionFailed);
  103. connection.addEventListener(
  104. JitsiConnectionEvents.CONNECTION_FAILED,
  105. connectionFailedHandler);
  106. connection.addEventListener(
  107. JitsiConnectionEvents.DISPLAY_NAME_REQUIRED,
  108. displayNameRequiredHandler
  109. );
  110. /* eslint-disable max-params */
  111. /**
  112. *
  113. */
  114. function connectionFailedHandler(error, message, credentials, details) {
  115. /* eslint-enable max-params */
  116. APP.store.dispatch(
  117. connectionFailed(
  118. connection, {
  119. credentials,
  120. details,
  121. message,
  122. name: error
  123. }));
  124. if (isFatalJitsiConnectionError(error)) {
  125. connection.removeEventListener(
  126. JitsiConnectionEvents.CONNECTION_FAILED,
  127. connectionFailedHandler);
  128. }
  129. }
  130. /**
  131. *
  132. */
  133. function unsubscribe() {
  134. connection.removeEventListener(
  135. JitsiConnectionEvents.CONNECTION_ESTABLISHED,
  136. handleConnectionEstablished);
  137. connection.removeEventListener(
  138. JitsiConnectionEvents.CONNECTION_FAILED,
  139. handleConnectionFailed);
  140. }
  141. /**
  142. *
  143. */
  144. function handleConnectionEstablished() {
  145. APP.store.dispatch(connectionEstablished(connection, Date.now()));
  146. unsubscribe();
  147. resolve(connection);
  148. }
  149. /**
  150. *
  151. */
  152. function handleConnectionFailed(err) {
  153. unsubscribe();
  154. logger.error('CONNECTION FAILED:', err);
  155. reject(err);
  156. }
  157. /**
  158. * Marks the display name for the prejoin screen as required.
  159. * This can happen if a user tries to join a room with lobby enabled.
  160. */
  161. function displayNameRequiredHandler() {
  162. APP.store.dispatch(setPrejoinDisplayNameRequired());
  163. }
  164. checkForAttachParametersAndConnect(id, password, connection);
  165. });
  166. }
  167. /**
  168. * Open JitsiConnection using provided credentials.
  169. * If retry option is true it will show auth dialog on PASSWORD_REQUIRED error.
  170. *
  171. * @param {object} options
  172. * @param {string} [options.id]
  173. * @param {string} [options.password]
  174. * @param {string} [options.roomName]
  175. * @param {boolean} [retry] if we should show auth dialog
  176. * on PASSWORD_REQUIRED error.
  177. *
  178. * @returns {Promise<JitsiConnection>}
  179. */
  180. export function openConnection({ id, password, retry, roomName }) {
  181. const usernameOverride
  182. = jitsiLocalStorage.getItem('xmpp_username_override');
  183. const passwordOverride
  184. = jitsiLocalStorage.getItem('xmpp_password_override');
  185. if (usernameOverride && usernameOverride.length > 0) {
  186. id = usernameOverride; // eslint-disable-line no-param-reassign
  187. }
  188. if (passwordOverride && passwordOverride.length > 0) {
  189. password = passwordOverride; // eslint-disable-line no-param-reassign
  190. }
  191. return connect(id, password, roomName).catch(err => {
  192. if (retry) {
  193. const { jwt } = APP.store.getState()['features/base/jwt'];
  194. if (err === JitsiConnectionErrors.PASSWORD_REQUIRED && !jwt) {
  195. return requestAuth(roomName);
  196. }
  197. }
  198. throw err;
  199. });
  200. }
  201. /**
  202. * Show Authentication Dialog and try to connect with new credentials.
  203. * If failed to connect because of PASSWORD_REQUIRED error
  204. * then ask for password again.
  205. * @param {string} [roomName] name of the conference room
  206. *
  207. * @returns {Promise<JitsiConnection>}
  208. */
  209. function requestAuth(roomName) {
  210. const config = APP.store.getState()['features/base/config'];
  211. if (isTokenAuthEnabled(config)) {
  212. // This Promise never resolves as user gets redirected to another URL
  213. return new Promise(() => redirectToTokenAuthService(roomName));
  214. }
  215. return new Promise(resolve => {
  216. const onSuccess = connection => {
  217. resolve(connection);
  218. };
  219. APP.store.dispatch(
  220. openDialog(LoginDialog, { onSuccess,
  221. roomName })
  222. );
  223. });
  224. }