| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 | 
							- // @flow
 - 
 - import { getLocalParticipant, PARTICIPANT_ROLE } from '../base/participants';
 - import { doGetJSON } from '../base/util';
 - 
 - declare var $: Function;
 - declare var interfaceConfig: Object;
 - 
 - const logger = require('jitsi-meet-logger').getLogger(__filename);
 - 
 - /**
 -  * Sends an ajax request to check if the phone number can be called.
 -  *
 -  * @param {string} dialNumber - The dial number to check for validity.
 -  * @param {string} dialOutAuthUrl - The endpoint to use for checking validity.
 -  * @returns {Promise} - The promise created by the request.
 -  */
 - export function checkDialNumber(
 -         dialNumber: string,
 -         dialOutAuthUrl: string
 - ): Promise<Object> {
 - 
 -     if (!dialOutAuthUrl) {
 -         // no auth url, let's say it is valid
 -         const response = {
 -             allow: true,
 -             phone: `+${dialNumber}`
 -         };
 - 
 -         return Promise.resolve(response);
 -     }
 - 
 -     const fullUrl = `${dialOutAuthUrl}?phone=${dialNumber}`;
 - 
 -     return new Promise((resolve, reject) => {
 -         $.getJSON(fullUrl)
 -             .then(resolve)
 -             .catch(reject);
 -     });
 - }
 - 
 - /**
 -  * Sends a GET request to obtain the conference ID necessary for identifying
 -  * which conference to join after diaing the dial-in service.
 -  *
 -  * @param {string} baseUrl - The url for obtaining the conference ID (pin) for
 -  * dialing into a conference.
 -  * @param {string} roomName - The conference name to find the associated
 -  * conference ID.
 -  * @param {string} mucURL - In which MUC the conference exists.
 -  * @returns {Promise} - The promise created by the request.
 -  */
 - export function getDialInConferenceID(
 -         baseUrl: string,
 -         roomName: string,
 -         mucURL: string
 - ): Promise<Object> {
 - 
 -     const conferenceIDURL = `${baseUrl}?conference=${roomName}@${mucURL}`;
 - 
 -     return doGetJSON(conferenceIDURL);
 - }
 - 
 - /**
 -  * Sends a GET request for phone numbers used to dial into a conference.
 -  *
 -  * @param {string} url - The service that returns confernce dial-in numbers.
 -  * @returns {Promise} - The promise created by the request. The returned numbers
 -  * may be an array of numbers or an object with countries as keys and arrays of
 -  * phone number strings.
 -  */
 - export function getDialInNumbers(url: string): Promise<*> {
 -     return doGetJSON(url);
 - }
 - 
 - /**
 -  * Removes all non-numeric characters from a string.
 -  *
 -  * @param {string} text - The string from which to remove all characters
 -  * except numbers.
 -  * @private
 -  * @returns {string} A string with only numbers.
 -  */
 - function getDigitsOnly(text: string = ''): string {
 -     return text.replace(/\D/g, '');
 - }
 - 
 - /**
 -  * Type of the options to use when sending a search query.
 -  */
 - export type GetInviteResultsOptions = {
 - 
 -     /**
 -      * The endpoint to use for checking phone number validity.
 -      */
 -     dialOutAuthUrl: string,
 - 
 -     /**
 -      * Whether or not to search for people.
 -      */
 -     addPeopleEnabled: boolean,
 - 
 -     /**
 -      * Whether or not to check phone numbers.
 -      */
 -     dialOutEnabled: boolean,
 - 
 -     /**
 -      * Array with the query types that will be executed -
 -      * "conferenceRooms" | "user" | "room".
 -      */
 -     peopleSearchQueryTypes: Array<string>,
 - 
 -     /**
 -      * The url to query for people.
 -      */
 -     peopleSearchUrl: string,
 - 
 -     /**
 -      * The jwt token to pass to the search service.
 -      */
 -     jwt: string
 - };
 - 
 - /**
 -  * Combines directory search with phone number validation to produce a single
 -  * set of invite search results.
 -  *
 -  * @param  {string} query - Text to search.
 -  * @param  {GetInviteResultsOptions} options - Options to use when searching.
 -  * @returns {Promise<*>}
 -  */
 - export function getInviteResultsForQuery(
 -         query: string,
 -         options: GetInviteResultsOptions
 - ): Promise<*> {
 - 
 -     const text = query.trim();
 - 
 -     const {
 -         dialOutAuthUrl,
 -         addPeopleEnabled,
 -         dialOutEnabled,
 -         peopleSearchQueryTypes,
 -         peopleSearchUrl,
 -         jwt
 -     } = options;
 - 
 -     let peopleSearchPromise;
 - 
 -     if (addPeopleEnabled && text) {
 -         peopleSearchPromise = searchDirectory(
 -             peopleSearchUrl,
 -             jwt,
 -             text,
 -             peopleSearchQueryTypes);
 -     } else {
 -         peopleSearchPromise = Promise.resolve([]);
 -     }
 - 
 - 
 -     const hasCountryCode = text.startsWith('+');
 -     let phoneNumberPromise;
 - 
 -     if (dialOutEnabled && isMaybeAPhoneNumber(text)) {
 -         let numberToVerify = text;
 - 
 -         // When the number to verify does not start with a +, we assume no
 -         // proper country code has been entered. In such a case, prepend 1
 -         // for the country code. The service currently takes care of
 -         // prepending the +.
 -         if (!hasCountryCode && !text.startsWith('1')) {
 -             numberToVerify = `1${numberToVerify}`;
 -         }
 - 
 -         // The validation service works properly when the query is digits
 -         // only so ensure only digits get sent.
 -         numberToVerify = getDigitsOnly(numberToVerify);
 - 
 -         phoneNumberPromise
 -             = checkDialNumber(numberToVerify, dialOutAuthUrl);
 -     } else {
 -         phoneNumberPromise = Promise.resolve({});
 -     }
 - 
 -     return Promise.all([ peopleSearchPromise, phoneNumberPromise ])
 -         .then(([ peopleResults, phoneResults ]) => {
 -             const results = [
 -                 ...peopleResults
 -             ];
 - 
 -             /**
 -              * This check for phone results is for the day the call to
 -              * searching people might return phone results as well. When
 -              * that day comes this check will make it so the server checks
 -              * are honored and the local appending of the number is not
 -              * done. The local appending of the phone number can then be
 -              * cleaned up when convenient.
 -              */
 -             const hasPhoneResult = peopleResults.find(
 -                 result => result.type === 'phone');
 - 
 -             if (!hasPhoneResult
 -                     && typeof phoneResults.allow === 'boolean') {
 -                 results.push({
 -                     allowed: phoneResults.allow,
 -                     country: phoneResults.country,
 -                     type: 'phone',
 -                     number: phoneResults.phone,
 -                     originalEntry: text,
 -                     showCountryCodeReminder: !hasCountryCode
 -                 });
 -             }
 - 
 -             return results;
 -         });
 - }
 - 
 - /**
 -  * Sends a post request to an invite service.
 -  *
 -  * @param {string} inviteServiceUrl - The invite service that generates the
 -  * invitation.
 -  * @param {string} inviteUrl - The url to the conference.
 -  * @param {string} jwt - The jwt token to pass to the search service.
 -  * @param {Immutable.List} inviteItems - The list of the "user" or "room"
 -  * type items to invite.
 -  * @returns {Promise} - The promise created by the request.
 -  */
 - function invitePeopleAndChatRooms( // eslint-disable-line max-params
 -         inviteServiceUrl: string,
 -         inviteUrl: string,
 -         jwt: string,
 -         inviteItems: Array<Object>
 - ): Promise<void> {
 - 
 -     if (!inviteItems || inviteItems.length === 0) {
 -         return Promise.resolve();
 -     }
 - 
 -     return new Promise((resolve, reject) => {
 -         $.post(
 -                 `${inviteServiceUrl}?token=${jwt}`,
 -                 JSON.stringify({
 -                     'invited': inviteItems,
 -                     'url': inviteUrl
 -                 }),
 -                 resolve,
 -                 'json')
 -             .fail((jqxhr, textStatus, error) => reject(error));
 -     });
 - }
 - 
 - /**
 -  * Determines if adding people is currently enabled.
 -  *
 -  * @param {boolean} state - Current state.
 -  * @returns {boolean} Indication of whether adding people is currently enabled.
 -  */
 - export function isAddPeopleEnabled(state: Object): boolean {
 -     const { isGuest } = state['features/base/jwt'];
 - 
 -     if (!isGuest) {
 -         // XXX The mobile/react-native app is capable of disabling the
 -         // adding/inviting of people in the current conference. Anyway, the
 -         // Web/React app does not have that capability so default appropriately.
 -         const { app } = state['features/app'];
 -         const addPeopleEnabled = app && app.props.addPeopleEnabled;
 - 
 -         return (
 -             (typeof addPeopleEnabled === 'undefined')
 -                 || Boolean(addPeopleEnabled));
 -     }
 - 
 -     return false;
 - }
 - 
 - /**
 -  * Determines if dial out is currently enabled or not.
 -  *
 -  * @param {boolean} state - Current state.
 -  * @returns {boolean} Indication of whether dial out is currently enabled.
 -  */
 - export function isDialOutEnabled(state: Object): boolean {
 -     const { conference } = state['features/base/conference'];
 -     const { isGuest } = state['features/base/jwt'];
 -     const { enableUserRolesBasedOnToken } = state['features/base/config'];
 -     const participant = getLocalParticipant(state);
 - 
 -     return participant && participant.role === PARTICIPANT_ROLE.MODERATOR
 -                 && conference && conference.isSIPCallingSupported()
 -                 && (!enableUserRolesBasedOnToken || !isGuest);
 - }
 - 
 - /**
 -  * Checks whether a string looks like it could be for a phone number.
 -  *
 -  * @param {string} text - The text to check whether or not it could be a
 -  * phone number.
 -  * @private
 -  * @returns {boolean} True if the string looks like it could be a phone
 -  * number.
 -  */
 - function isMaybeAPhoneNumber(text: string): boolean {
 -     if (!isPhoneNumberRegex().test(text)) {
 -         return false;
 -     }
 - 
 -     const digits = getDigitsOnly(text);
 - 
 -     return Boolean(digits.length);
 - }
 - 
 - /**
 -  * RegExp to use to determine if some text might be a phone number.
 -  *
 -  * @returns {RegExp}
 -  */
 - function isPhoneNumberRegex(): RegExp {
 -     let regexString = '^[0-9+()-\\s]*$';
 - 
 -     if (typeof interfaceConfig !== 'undefined') {
 -         regexString = interfaceConfig.PHONE_NUMBER_REGEX || regexString;
 -     }
 - 
 -     return new RegExp(regexString);
 - }
 - 
 - /**
 -  * Sends an ajax request to a directory service.
 -  *
 -  * @param {string} serviceUrl - The service to query.
 -  * @param {string} jwt - The jwt token to pass to the search service.
 -  * @param {string} text - Text to search.
 -  * @param {Array<string>} queryTypes - Array with the query types that will be
 -  * executed - "conferenceRooms" | "user" | "room".
 -  * @returns {Promise} - The promise created by the request.
 -  */
 - export function searchDirectory( // eslint-disable-line max-params
 -         serviceUrl: string,
 -         jwt: string,
 -         text: string,
 -         queryTypes: Array<string> = [ 'conferenceRooms', 'user', 'room' ]
 - ): Promise<Array<Object>> {
 - 
 -     const query = encodeURIComponent(text);
 -     const queryTypesString = encodeURIComponent(JSON.stringify(queryTypes));
 - 
 -     return fetch(`${serviceUrl}?query=${query}&queryTypes=${
 -         queryTypesString}&jwt=${jwt}`)
 -             .then(response => {
 -                 const jsonify = response.json();
 - 
 -                 if (response.ok) {
 -                     return jsonify;
 -                 }
 - 
 -                 return jsonify
 -                     .then(result => Promise.reject(result));
 -             })
 -             .catch(error => {
 -                 logger.error(
 -                     'Error searching directory:', error);
 - 
 -                 return Promise.reject(error);
 -             });
 - }
 - 
 - /**
 -  * Type of the options to use when sending invites.
 -  */
 - export type SendInvitesOptions = {
 - 
 -     /**
 -      * Conference object used to dial out.
 -      */
 -     conference: Object,
 - 
 -     /**
 -      * The URL to send invites through.
 -      */
 -     inviteServiceUrl: string,
 - 
 -     /**
 -      * The URL sent with each invite.
 -      */
 -     inviteUrl: string,
 - 
 -     /**
 -      * The function to use to invite video rooms.
 -      *
 -      * @param  {Object} The conference to which the video rooms should be
 -      * invited.
 -      * @param  {Array<Object>} The list of rooms that should be invited.
 -      * @returns {void}
 -      */
 -     inviteVideoRooms: (Object, Array<Object>) => void,
 - 
 -     /**
 -      * The jwt token to pass to the invite service.
 -      */
 -     jwt: string
 - };
 - 
 - /**
 -  * Send invites for a list of items (may be a combination of users, rooms, phone
 -  * numbers, and video rooms).
 -  *
 -  * @param  {Array<Object>} invites - Items for which invites should be sent.
 -  * @param  {SendInvitesOptions} options - Options to use when sending the
 -  * provided invites.
 -  * @returns {Promise} Promise containing the list of invites that were not sent.
 -  */
 - export function sendInvitesForItems(
 -         invites: Array<Object>,
 -         options: SendInvitesOptions
 - ): Promise<Array<Object>> {
 - 
 -     const {
 -         conference,
 -         inviteServiceUrl,
 -         inviteUrl,
 -         inviteVideoRooms,
 -         jwt
 -     } = options;
 - 
 -     let allInvitePromises = [];
 -     let invitesLeftToSend = [ ...invites ];
 - 
 -     // First create all promises for dialing out.
 -     if (conference) {
 -         const phoneNumbers = invitesLeftToSend.filter(
 -             item => item.type === 'phone');
 - 
 -         // For each number, dial out. On success, remove the number from
 -         // {@link invitesLeftToSend}.
 -         const phoneInvitePromises = phoneNumbers.map(item => {
 -             const numberToInvite = getDigitsOnly(item.number);
 - 
 -             return conference.dial(numberToInvite)
 -                     .then(() => {
 -                         invitesLeftToSend
 -                             = invitesLeftToSend.filter(invite =>
 -                                 invite !== item);
 -                     })
 -                     .catch(error => logger.error(
 -                         'Error inviting phone number:', error));
 - 
 -         });
 - 
 -         allInvitePromises = allInvitePromises.concat(phoneInvitePromises);
 -     }
 - 
 -     const usersAndRooms = invitesLeftToSend.filter(item =>
 -         item.type === 'user' || item.type === 'room');
 - 
 -     if (usersAndRooms.length) {
 -         // Send a request to invite all the rooms and users. On success,
 -         // filter all rooms and users from {@link invitesLeftToSend}.
 -         const peopleInvitePromise = invitePeopleAndChatRooms(
 -             inviteServiceUrl,
 -             inviteUrl,
 -             jwt,
 -             usersAndRooms)
 -             .then(() => {
 -                 invitesLeftToSend = invitesLeftToSend.filter(item =>
 -                     item.type !== 'user' && item.type !== 'room');
 -             })
 -             .catch(error => logger.error(
 -                 'Error inviting people:', error));
 - 
 -         allInvitePromises.push(peopleInvitePromise);
 -     }
 - 
 -     // Sipgw calls are fire and forget. Invite them to the conference
 -     // then immediately remove them from {@link invitesLeftToSend}.
 -     const vrooms = invitesLeftToSend.filter(item =>
 -         item.type === 'videosipgw');
 - 
 -     conference
 -         && vrooms.length > 0
 -         && inviteVideoRooms(conference, vrooms);
 - 
 -     invitesLeftToSend = invitesLeftToSend.filter(item =>
 -         item.type !== 'videosipgw');
 - 
 -     return Promise.all(allInvitePromises)
 -         .then(() => invitesLeftToSend);
 - }
 
 
  |