| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 | 
							- /* global $, APP, config, Strophe*/
 - var Moderator = require("./moderator");
 - var EventEmitter = require("events");
 - var Recording = require("./recording");
 - var SDP = require("./SDP");
 - var Settings = require("../settings/Settings");
 - var Pako = require("pako");
 - var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
 - var RTCEvents = require("../../service/RTC/RTCEvents");
 - var XMPPEvents = require("../../service/xmpp/XMPPEvents");
 - var retry = require('retry');
 - 
 - var eventEmitter = new EventEmitter();
 - var connection = null;
 - var authenticatedUser = false;
 - 
 - function connect(jid, password) {
 - 
 -     var faultTolerantConnect = retry.operation({
 -         retries: 3
 -     });
 - 
 -     // fault tolerant connect
 -     faultTolerantConnect.attempt(function () {
 - 
 -         connection = XMPP.createConnection();
 -         Moderator.setConnection(connection);
 - 
 -         connection.jingle.pc_constraints = APP.RTC.getPCConstraints();
 -         if (config.useIPv6) {
 -             // https://code.google.com/p/webrtc/issues/detail?id=2828
 -             if (!connection.jingle.pc_constraints.optional)
 -                 connection.jingle.pc_constraints.optional = [];
 -             connection.jingle.pc_constraints.optional.push({googIPv6: true});
 -         }
 - 
 -         // Include user info in MUC presence
 -         var settings = Settings.getSettings();
 -         if (settings.email) {
 -             connection.emuc.addEmailToPresence(settings.email);
 -         }
 -         if (settings.uid) {
 -             connection.emuc.addUserIdToPresence(settings.uid);
 -         }
 -         if (settings.displayName) {
 -             connection.emuc.addDisplayNameToPresence(settings.displayName);
 -         }
 - 
 - 
 -         // connection.connect() starts the connection process.
 -         //
 -         // As the connection process proceeds, the user supplied callback will
 -         // be triggered multiple times with status updates. The callback should
 -         // take two arguments - the status code and the error condition.
 -         //
 -         // The status code will be one of the values in the Strophe.Status
 -         // constants. The error condition will be one of the conditions defined
 -         // in RFC 3920 or the condition ‘strophe-parsererror’.
 -         //
 -         // The Parameters wait, hold and route are optional and only relevant
 -         // for BOSH connections. Please see XEP 124 for a more detailed
 -         // explanation of the optional parameters.
 -         //
 -         // Connection status constants for use by the connection handler
 -         // callback.
 -         //
 -         //  Status.ERROR - An error has occurred (websockets specific)
 -         //  Status.CONNECTING - The connection is currently being made
 -         //  Status.CONNFAIL - The connection attempt failed
 -         //  Status.AUTHENTICATING - The connection is authenticating
 -         //  Status.AUTHFAIL - The authentication attempt failed
 -         //  Status.CONNECTED - The connection has succeeded
 -         //  Status.DISCONNECTED - The connection has been terminated
 -         //  Status.DISCONNECTING - The connection is currently being terminated
 -         //  Status.ATTACHED - The connection has been attached
 - 
 -         var anonymousConnectionFailed = false;
 -         var connectionFailed = false;
 -         var lastErrorMsg;
 -         connection.connect(jid, password, function (status, msg) {
 -             console.log('Strophe status changed to',
 -                 Strophe.getStatusString(status), msg);
 -             if (status === Strophe.Status.CONNECTED) {
 -                 if (config.useStunTurn) {
 -                     connection.jingle.getStunAndTurnCredentials();
 -                 }
 - 
 -                 console.info("My Jabber ID: " + connection.jid);
 - 
 -                 if (password)
 -                     authenticatedUser = true;
 -                 maybeDoJoin();
 -             } else if (status === Strophe.Status.CONNFAIL) {
 -                 if (msg === 'x-strophe-bad-non-anon-jid') {
 -                     anonymousConnectionFailed = true;
 -                 } else {
 -                     connectionFailed = true;
 -                 }
 -                 lastErrorMsg = msg;
 -             } else if (status === Strophe.Status.DISCONNECTED) {
 -                 if (anonymousConnectionFailed) {
 -                     // prompt user for username and password
 -                     XMPP.promptLogin();
 -                 } else {
 - 
 -                     // Strophe already has built-in HTTP/BOSH error handling and
 -                     // request retry logic. Requests are resent automatically
 -                     // until their error count reaches 5. Strophe.js disconnects
 -                     // if the error count is > 5. We are not replicating this
 -                     // here.
 -                     //
 -                     // The "problem" is that failed HTTP/BOSH requests don't
 -                     // trigger a callback with a status update, so when a
 -                     // callback with status Strophe.Status.DISCONNECTED arrives,
 -                     // we can't be sure if it's a graceful disconnect or if it's
 -                     // triggered by some HTTP/BOSH error.
 -                     //
 -                     // But that's a minor issue in Jitsi Meet as we never
 -                     // disconnect anyway, not even when the user closes the
 -                     // browser window (which is kind of wrong, but the point is
 -                     // that we should never ever get disconnected).
 -                     //
 -                     // On the other hand, failed connections due to XMPP layer
 -                     // errors, trigger a callback with status Strophe.Status.CONNFAIL.
 -                     //
 -                     // Here we implement retry logic for failed connections due
 -                     // to XMPP layer errors and we display an error to the user
 -                     // if we get disconnected from the XMPP server permanently.
 - 
 -                     // If the connection failed, retry.
 -                     if (connectionFailed &&
 -                         faultTolerantConnect.retry("connection-failed")) {
 -                         return;
 -                     }
 - 
 -                     // If we failed to connect to the XMPP server, fire an event
 -                     // to let all the interested module now about it.
 -                     eventEmitter.emit(XMPPEvents.CONNECTION_FAILED,
 -                         msg ? msg : lastErrorMsg);
 -                 }
 -             } else if (status === Strophe.Status.AUTHFAIL) {
 -                 // wrong password or username, prompt user
 -                 XMPP.promptLogin();
 - 
 -             }
 -         });
 -     });
 - }
 - 
 - 
 - function maybeDoJoin() {
 -     if (connection && connection.connected &&
 -         Strophe.getResourceFromJid(connection.jid) &&
 -         (APP.RTC.localAudio || APP.RTC.localVideo)) {
 -         // .connected is true while connecting?
 -         doJoin();
 -     }
 - }
 - 
 - function doJoin() {
 -     eventEmitter.emit(XMPPEvents.READY_TO_JOIN);
 - }
 - 
 - function initStrophePlugins()
 - {
 -     require("./strophe.emuc")(XMPP, eventEmitter);
 -     require("./strophe.jingle")(XMPP, eventEmitter);
 -     require("./strophe.moderate")(XMPP, eventEmitter);
 -     require("./strophe.util")();
 -     require("./strophe.rayo")();
 -     require("./strophe.logger")();
 - }
 - 
 - /**
 -  * If given <tt>localStream</tt> is video one this method will advertise it's
 -  * video type in MUC presence.
 -  * @param localStream new or modified <tt>LocalStream</tt>.
 -  */
 - function broadcastLocalVideoType(localStream) {
 -     if (localStream.videoType)
 -         XMPP.addToPresence('videoType', localStream.videoType);
 - }
 - 
 - function registerListeners() {
 -     APP.RTC.addStreamListener(
 -         function (localStream) {
 -             maybeDoJoin();
 -             broadcastLocalVideoType(localStream);
 -         },
 -         StreamEventTypes.EVENT_TYPE_LOCAL_CREATED
 -     );
 -     APP.RTC.addStreamListener(
 -         broadcastLocalVideoType,
 -         StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED
 -     );
 -     APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
 -         XMPP.addToPresence("devices", devices);
 -     });
 - }
 - 
 - var unload = (function () {
 -     var unloaded = false;
 - 
 -     return function () {
 -         if (unloaded) { return; }
 -         unloaded = true;
 - 
 -         if (connection && connection.connected) {
 -             // ensure signout
 -             $.ajax({
 -                 type: 'POST',
 -                 url: config.bosh,
 -                 async: false,
 -                 cache: false,
 -                 contentType: 'application/xml',
 -                 data: "<body rid='" +
 -                     (connection.rid || connection._proto.rid) +
 -                     "' xmlns='http://jabber.org/protocol/httpbind' sid='" +
 -                     (connection.sid || connection._proto.sid)  +
 -                     "' type='terminate'>" +
 -                 "<presence xmlns='jabber:client' type='unavailable'/>" +
 -                 "</body>",
 -                 success: function (data) {
 -                     console.log('signed out');
 -                     console.log(data);
 -                 },
 -                 error: function (XMLHttpRequest, textStatus, errorThrown) {
 -                     console.log('signout error',
 -                         textStatus + ' (' + errorThrown + ')');
 -                 }
 -             });
 -         }
 -         XMPP.disposeConference(true);
 -     };
 - })();
 - 
 - function setupEvents() {
 -     // In recent versions of FF the 'beforeunload' event is not fired when the
 -     // window or the tab is closed. It is only fired when we leave the page
 -     // (change URL). If this participant doesn't unload properly, then it
 -     // becomes a ghost for the rest of the participants that stay in the
 -     // conference. Thankfully handling the 'unload' event in addition to the
 -     // 'beforeunload' event seems to guarantee the execution of the 'unload'
 -     // method at least once.
 -     //
 -     // The 'unload' method can safely be run multiple times, it will actually do
 -     // something only the first time that it's run, so we're don't have to worry
 -     // about browsers that fire both events.
 - 
 -     $(window).bind('beforeunload', unload);
 -     $(window).bind('unload', unload);
 - }
 - 
 - var XMPP = {
 -     getConnection: function(){ return connection; },
 -     sessionTerminated: false,
 - 
 -     /**
 -      * XMPP connection status
 -      */
 -     Status: Strophe.Status,
 - 
 -     /**
 -      * Remembers if we were muted by the focus.
 -      * @type {boolean}
 -      */
 -     forceMuted: false,
 -     start: function () {
 -         setupEvents();
 -         initStrophePlugins();
 -         registerListeners();
 -         Moderator.init(this, eventEmitter);
 -         Recording.init();
 -         var configDomain = config.hosts.anonymousdomain || config.hosts.domain;
 -         // Force authenticated domain if room is appended with '?login=true'
 -         if (config.hosts.anonymousdomain &&
 -             window.location.search.indexOf("login=true") !== -1) {
 -             configDomain = config.hosts.domain;
 -         }
 -         var jid = configDomain || window.location.hostname;
 -         connect(jid, null);
 -     },
 -     createConnection: function () {
 -         var bosh = config.bosh || '/http-bind';
 - 
 -         return new Strophe.Connection(bosh);
 -     },
 -     getStatusString: function (status) {
 -         return Strophe.getStatusString(status);
 -     },
 -     promptLogin: function () {
 -         eventEmitter.emit(XMPPEvents.PROMPT_FOR_LOGIN);
 -     },
 -     joinRoom: function(roomName, useNicks, nick) {
 -         var roomjid = roomName;
 - 
 -         if (useNicks) {
 -             if (nick) {
 -                 roomjid += '/' + nick;
 -             } else {
 -                 roomjid += '/' + Strophe.getNodeFromJid(connection.jid);
 -             }
 -         } else {
 -             var tmpJid = Strophe.getNodeFromJid(connection.jid);
 - 
 -             if(!authenticatedUser)
 -                 tmpJid = tmpJid.substr(0, 8);
 - 
 -             roomjid += '/' + tmpJid;
 -         }
 -         connection.emuc.doJoin(roomjid);
 -     },
 -     myJid: function () {
 -         if(!connection)
 -             return null;
 -         return connection.emuc.myroomjid;
 -     },
 -     myResource: function () {
 -         if(!connection || ! connection.emuc.myroomjid)
 -             return null;
 -         return Strophe.getResourceFromJid(connection.emuc.myroomjid);
 -     },
 -     disposeConference: function (onUnload) {
 -         var handler = connection.jingle.activecall;
 -         if (handler && handler.peerconnection) {
 -             // FIXME: probably removing streams is not required and close() should
 -             // be enough
 -             if (APP.RTC.localAudio) {
 -                 handler.peerconnection.removeStream(
 -                     APP.RTC.localAudio.getOriginalStream(), onUnload);
 -             }
 -             if (APP.RTC.localVideo) {
 -                 handler.peerconnection.removeStream(
 -                     APP.RTC.localVideo.getOriginalStream(), onUnload);
 -             }
 -             handler.peerconnection.close();
 -         }
 -         eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE, onUnload);
 -         connection.jingle.activecall = null;
 -         if (!onUnload) {
 -             this.sessionTerminated = true;
 -             connection.emuc.doLeave();
 -         }
 -     },
 -     addListener: function(type, listener) {
 -         eventEmitter.on(type, listener);
 -     },
 -     removeListener: function (type, listener) {
 -         eventEmitter.removeListener(type, listener);
 -     },
 -     allocateConferenceFocus: function(roomName, callback) {
 -         Moderator.allocateConferenceFocus(roomName, callback);
 -     },
 -     getLoginUrl: function (roomName, callback) {
 -         Moderator.getLoginUrl(roomName, callback);
 -     },
 -     getPopupLoginUrl: function (roomName, callback) {
 -         Moderator.getPopupLoginUrl(roomName, callback);
 -     },
 -     isModerator: function () {
 -         return Moderator.isModerator();
 -     },
 -     isSipGatewayEnabled: function () {
 -         return Moderator.isSipGatewayEnabled();
 -     },
 -     isExternalAuthEnabled: function () {
 -         return Moderator.isExternalAuthEnabled();
 -     },
 -     isConferenceInProgress: function () {
 -         return connection && connection.jingle.activecall &&
 -             connection.jingle.activecall.peerconnection;
 -     },
 -     switchStreams: function (stream, oldStream, callback, isAudio) {
 -         if (this.isConferenceInProgress()) {
 -             // FIXME: will block switchInProgress on true value in case of exception
 -             connection.jingle.activecall.switchStreams(stream, oldStream, callback, isAudio);
 -         } else {
 -             // We are done immediately
 -             console.warn("No conference handler or conference not started yet");
 -             callback();
 -         }
 -     },
 -     sendVideoInfoPresence: function (mute) {
 -         if(!connection)
 -             return;
 -         connection.emuc.addVideoInfoToPresence(mute);
 -         connection.emuc.sendPresence();
 -     },
 -     setVideoMute: function (mute, callback, options) {
 -         if(!connection)
 -             return;
 -         var self = this;
 -         var localCallback = function (mute) {
 -             self.sendVideoInfoPresence(mute);
 -             return callback(mute);
 -         };
 - 
 -         if(connection.jingle.activecall)
 -         {
 -             connection.jingle.activecall.setVideoMute(
 -                 mute, localCallback, options);
 -         }
 -         else {
 -             localCallback(mute);
 -         }
 - 
 -     },
 -     setAudioMute: function (mute, callback) {
 -         if (!(connection && APP.RTC.localAudio)) {
 -             return false;
 -         }
 - 
 -         if (this.forceMuted && !mute) {
 -             console.info("Asking focus for unmute");
 -             connection.moderate.setMute(connection.emuc.myroomjid, mute);
 -             // FIXME: wait for result before resetting muted status
 -             this.forceMuted = false;
 -         }
 - 
 -         if (mute == APP.RTC.localAudio.isMuted()) {
 -             // Nothing to do
 -             return true;
 -         }
 - 
 -         APP.RTC.localAudio.setMute(mute);
 -         this.sendAudioInfoPresence(mute, callback);
 -         return true;
 -     },
 -     sendAudioInfoPresence: function(mute, callback) {
 -         if(connection) {
 -             connection.emuc.addAudioInfoToPresence(mute);
 -             connection.emuc.sendPresence();
 -         }
 -         callback();
 -         return true;
 -     },
 -     toggleRecording: function (tokenEmptyCallback,
 -                                recordingStateChangeCallback) {
 -         Recording.toggleRecording(tokenEmptyCallback,
 -             recordingStateChangeCallback, connection);
 -     },
 -     addToPresence: function (name, value, dontSend) {
 -         switch (name) {
 -             case "displayName":
 -                 connection.emuc.addDisplayNameToPresence(value);
 -                 break;
 -             case "prezi":
 -                 connection.emuc.addPreziToPresence(value, 0);
 -                 break;
 -             case "preziSlide":
 -                 connection.emuc.addCurrentSlideToPresence(value);
 -                 break;
 -             case "connectionQuality":
 -                 connection.emuc.addConnectionInfoToPresence(value);
 -                 break;
 -             case "email":
 -                 connection.emuc.addEmailToPresence(value);
 -                 break;
 -             case "devices":
 -                 connection.emuc.addDevicesToPresence(value);
 -                 break;
 -             case "videoType":
 -                 connection.emuc.addVideoTypeToPresence(value);
 -                 break;
 -             case "startMuted":
 -                 if(!Moderator.isModerator())
 -                     return;
 -                 connection.emuc.addStartMutedToPresence(value[0],
 -                     value[1]);
 -                 break;
 -             default :
 -                 console.log("Unknown tag for presence: " + name);
 -                 return;
 -         }
 -         if (!dontSend)
 -             connection.emuc.sendPresence();
 -     },
 -     /**
 -      * Sends 'data' as a log message to the focus. Returns true iff a message
 -      * was sent.
 -      * @param data
 -      * @returns {boolean} true iff a message was sent.
 -      */
 -     sendLogs: function (data) {
 -         if(!connection.emuc.focusMucJid)
 -             return false;
 - 
 -         var deflate = true;
 - 
 -         var content = JSON.stringify(data);
 -         if (deflate) {
 -             content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
 -         }
 -         content = Base64.encode(content);
 -         // XEP-0337-ish
 -         var message = $msg({to: connection.emuc.focusMucJid, type: 'normal'});
 -         message.c('log', { xmlns: 'urn:xmpp:eventlog',
 -             id: 'PeerConnectionStats'});
 -         message.c('message').t(content).up();
 -         if (deflate) {
 -             message.c('tag', {name: "deflated", value: "true"}).up();
 -         }
 -         message.up();
 - 
 -         connection.send(message);
 -         return true;
 -     },
 -     // Gets the logs from strophe.jingle.
 -     getJingleLog: function () {
 -         return connection.jingle ? connection.jingle.getLog() : {};
 -     },
 -     // Gets the logs from strophe.
 -     getXmppLog: function () {
 -         return connection.logger ? connection.logger.log : null;
 -     },
 -     getPrezi: function () {
 -         return connection.emuc.getPrezi(this.myJid());
 -     },
 -     removePreziFromPresence: function () {
 -         connection.emuc.removePreziFromPresence();
 -         connection.emuc.sendPresence();
 -     },
 -     sendChatMessage: function (message, nickname) {
 -         connection.emuc.sendMessage(message, nickname);
 -     },
 -     setSubject: function (topic) {
 -         connection.emuc.setSubject(topic);
 -     },
 -     lockRoom: function (key, onSuccess, onError, onNotSupported) {
 -         connection.emuc.lockRoom(key, onSuccess, onError, onNotSupported);
 -     },
 -     dial: function (to, from, roomName,roomPass) {
 -         connection.rayo.dial(to, from, roomName,roomPass);
 -     },
 -     setMute: function (jid, mute) {
 -         connection.moderate.setMute(jid, mute);
 -     },
 -     eject: function (jid) {
 -         connection.moderate.eject(jid);
 -     },
 -     logout: function (callback) {
 -         Moderator.logout(callback);
 -     },
 -     findJidFromResource: function (resource) {
 -         return connection.emuc.findJidFromResource(resource);
 -     },
 -     getMembers: function () {
 -         return connection.emuc.members;
 -     },
 -     getJidFromSSRC: function (ssrc) {
 -         if (!this.isConferenceInProgress())
 -             return null;
 -         return connection.jingle.activecall.getSsrcOwner(ssrc);
 -     },
 -     // Returns true iff we have joined the MUC.
 -     isMUCJoined: function () {
 -         return connection.emuc.joined;
 -     },
 -     getSessions: function () {
 -         return connection.jingle.sessions;
 -     },
 -     removeStream: function (stream) {
 -         if (!this.isConferenceInProgress())
 -             return;
 -         connection.jingle.activecall.peerconnection.removeStream(stream);
 -     }
 - };
 - 
 - module.exports = XMPP;
 
 
  |