diff --git a/lang/main.json b/lang/main.json index 21218c24d..1de3a98fe 100644 --- a/lang/main.json +++ b/lang/main.json @@ -132,6 +132,7 @@ "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference could set a password.", "joinError": "Oops! We couldn't join the conference. There might be some problem with security configuration. Please contact service administrator.", "connectError": "Oops! Something went wrong and we couldn't connect to the conference.", + "connecting": "Connecting", "error": "Error", "detectext": "Error when trying to detect desktopsharing extension.", "failtoinstall": "Failed to install desktop sharing extension", @@ -159,6 +160,7 @@ "AuthMsg": "Authentication is required to create room:
__room__
You can either authenticate to create the room or just wait for someone else to do so.", "Authenticate": "Authenticate", "Cancel": "Cancel", + "retry": "Retry", "logoutTitle" : "Logout", "logoutQuestion" : "Are you sure you want to logout and stop the conference?", "sessTerminated": "Session Terminated", @@ -216,5 +218,20 @@ "Talk to you in a sec!" ], "and": "and" + }, + "connection": + { + "ERROR": "Error", + "CONNECTING": "Connecting", + "CONNFAIL": "Connection failed", + "AUTHENTICATING": "Authenticating", + "AUTHFAIL": "Authentication failed", + "CONNECTED": "Connected", + "DISCONNECTED": "Disconnected", + "DISCONNECTING": "Disconnecting", + "ATTACHED": "Attached", + "FETCH_SESSION_ID": "Obtaining session-id...", + "GOT_SESSION_ID": "Obtaining session-id... Done", + "GET_SESSION_ID_ERROR": "Get session-id error: " } } diff --git a/modules/UI/authentication/Authentication.js b/modules/UI/authentication/Authentication.js index 8ff2b3d40..ec7770dba 100644 --- a/modules/UI/authentication/Authentication.js +++ b/modules/UI/authentication/Authentication.js @@ -1,4 +1,7 @@ -/* global $, APP */ +/* global $, APP*/ + +var LoginDialog = require('./LoginDialog'); +var Moderator = require('../../xmpp/moderator'); /* Initial "authentication required" dialog */ var authDialog = null; @@ -50,6 +53,37 @@ var Authentication = { authenticationWindow = null; } }, + xmppAuthenticate: function () { + + var loginDialog = LoginDialog.show( + function (connection, state) { + if (!state) { + // User cancelled + loginDialog.close(); + return; + } else if (state == APP.xmpp.Status.CONNECTED) { + + loginDialog.close(); + + Authentication.stopInterval(); + Authentication.closeAuthenticationDialog(); + + // Close the connection as anonymous one will be used + // to create the conference. Session-id will authorize + // the request. + connection.disconnect(); + + var roomName = APP.UI.generateRoomName(); + Moderator.allocateConferenceFocus(roomName, function () { + // If it's not "on the fly" authentication now join + // the conference room + if (!APP.xmpp.getMUCJoined()) { + APP.UI.checkForNicknameAndJoin(); + } + }); + } + }, true); + }, focusAuthenticationWindow: function () { // If auth window exists just bring it to the front if (authenticationWindow) { @@ -60,7 +94,7 @@ var Authentication = { closeAuthenticationDialog: function () { // Close authentication dialog if opened if (authDialog) { - APP.UI.messageHandler.closeDialog(); + authDialog.close(); authDialog = null; } }, @@ -70,10 +104,7 @@ var Authentication = { // On closed function () { // Close authentication dialog if opened - if (authDialog) { - messageHandler.closeDialog(); - authDialog = null; - } + Authentication.closeAuthenticationDialog(); callback(); authenticationWindow = null; }); diff --git a/modules/UI/authentication/LoginDialog.js b/modules/UI/authentication/LoginDialog.js new file mode 100644 index 000000000..f2d7ce2f9 --- /dev/null +++ b/modules/UI/authentication/LoginDialog.js @@ -0,0 +1,228 @@ +/* global $, APP, config*/ + +var XMPP = require('../../xmpp/xmpp'); +var Moderator = require('../../xmpp/moderator'); + +//FIXME: use LoginDialog to add retries to XMPP.connect method used when +// anonymous domain is not enabled + +/** + * Creates new Dialog instance. + * @param callback function(Strophe.Connection, Strophe.Status) called + * when we either fail to connect or succeed(check Strophe.Status). + * @param obtainSession true if we want to send ConferenceIQ to Jicofo + * in order to create session-id after the connection is established. + * @constructor + */ +function Dialog(callback, obtainSession) { + + var self = this; + + var stop = false; + + var connection = APP.xmpp.createConnection(); + + var message = '

'; + message += APP.translation.translateString("dialog.passwordRequired"); + message += '

' + + '' + + ''; + + var okButton = APP.translation.generateTranslatonHTML("dialog.Ok"); + + var cancelButton = APP.translation.generateTranslatonHTML("dialog.Cancel"); + + var states = { + login: { + html: message, + buttons: [ + { title: okButton, value: true}, + { title: cancelButton, value: false} + ], + focus: ':input:first', + submit: function (e, v, m, f) { + e.preventDefault(); + if (v) { + var jid = f.username; + var password = f.password; + if (jid && password) { + stop = false; + connection.reset(); + connDialog.goToState('connecting'); + connection.connect(jid, password, stateHandler); + } + } else { + // User cancelled + stop = true; + callback(); + } + } + }, + connecting: { + title: APP.translation.translateString('dialog.connecting'), + html: '
', + buttons: [], + defaultButton: 0 + }, + finished: { + title: APP.translation.translateString('dialog.error'), + html: '
', + buttons: [ + { + title: APP.translation.translateString('dialog.retry'), + value: 'retry' + }, + { + title: APP.translation.translateString('dialog.Cancel'), + value: 'cancel' + }, + ], + defaultButton: 0, + submit: function (e, v, m, f) { + e.preventDefault(); + if (v === 'retry') + connDialog.goToState('login'); + else + callback(); + } + } + }; + + var connDialog + = APP.UI.messageHandler.openDialogWithStates(states, + { persistent: true, closeText: '' }, null); + + var stateHandler = function (status, message) { + if (stop) { + return; + } + + var translateKey = "connection." + XMPP.getStatusString(status); + var statusStr = APP.translation.translateString(translateKey); + + // Display current state + var connectionStatus = + connDialog.getState('connecting').find('#connectionStatus'); + + connectionStatus.text(statusStr); + + switch (status) { + case XMPP.Status.CONNECTED: + + stop = true; + if (!obtainSession) { + callback(connection, status); + return; + } + // Obtaining session-id status + connectionStatus.text( + APP.translation.translateString( + 'connection.FETCH_SESSION_ID')); + + // Authenticate with Jicofo and obtain session-id + var roomName = APP.UI.generateRoomName(); + + // Jicofo will return new session-id when connected + // from authenticated domain + connection.sendIQ( + Moderator.createConferenceIq(roomName), + function (result) { + + connectionStatus.text( + APP.translation.translateString( + 'connection.GOT_SESSION_ID')); + + stop = true; + + // Parse session-id + Moderator.parseSessionId(result); + + callback(connection, status); + }, + function (error) { + console.error("Auth on the fly failed", error); + + stop = true; + + var errorMsg = + APP.translation.translateString( + 'connection.GET_SESSION_ID_ERROR') + + $(error).find('>error').attr('code'); + + self.displayError(errorMsg); + + connection.disconnect(); + }); + + break; + case XMPP.Status.AUTHFAIL: + case XMPP.Status.CONNFAIL: + case XMPP.Status.DISCONNECTED: + + stop = true; + + callback(connection, status); + + var errorMessage = statusStr; + + if (message) + { + errorMessage += ': ' + message; + } + self.displayError(errorMessage); + + break; + default: + break; + } + }; + + /** + * Displays error message in 'finished' state which allows either to cancel + * or retry. + * @param message the final message to be displayed. + */ + this.displayError = function (message) { + + var finishedState = connDialog.getState('finished'); + + var errorMessageElem = finishedState.find('#errorMessage'); + errorMessageElem.text(message); + + connDialog.goToState('finished'); + }; + + /** + * Closes LoginDialog. + */ + this.close = function () { + stop = true; + connDialog.close(); + }; +} + +var LoginDialog = { + + /** + * Displays login prompt used to establish new XMPP connection. Given + * callback(Strophe.Connection, Strophe.Status) function will be + * called when we connect successfully(status === CONNECTED) or when we fail + * to do so. On connection failure program can call Dialog.close() method in + * order to cancel or do nothing to let the user retry. + * @param callback function(Strophe.Connection, Strophe.Status) + * called when we either fail to connect or succeed(check + * Strophe.Status). + * @param obtainSession true if we want to send ConferenceIQ to + * Jicofo in order to create session-id after the connection is + * established. + * @returns {Dialog} + */ + show: function (callback, obtainSession) { + return new Dialog(callback, obtainSession); + } +}; + +module.exports = LoginDialog; \ No newline at end of file diff --git a/modules/UI/toolbars/Toolbar.js b/modules/UI/toolbars/Toolbar.js index 549cf47b6..08a5b8532 100644 --- a/modules/UI/toolbars/Toolbar.js +++ b/modules/UI/toolbars/Toolbar.js @@ -264,12 +264,6 @@ var Toolbar = (function (my) { loggedIn = true; } - //FIXME: XMPP authentication need improvements for "live" login - if (!APP.xmpp.isExternalAuthEnabled() && !loggedIn) - { - authenticationEnabled = false; - } - Toolbar.showAuthenticateButton(authenticationEnabled); if (authenticationEnabled) { @@ -292,6 +286,10 @@ var Toolbar = (function (my) { my.authenticateClicked = function () { Authentication.focusAuthenticationWindow(); + if (!APP.xmpp.isExternalAuthEnabled()) { + Authentication.xmppAuthenticate(); + return; + } // Get authentication URL if (!APP.xmpp.getMUCJoined()) { APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) { diff --git a/modules/UI/util/MessageHandler.js b/modules/UI/util/MessageHandler.js index e3a3e97d5..52f7acad5 100644 --- a/modules/UI/util/MessageHandler.js +++ b/modules/UI/util/MessageHandler.js @@ -94,7 +94,7 @@ var messageHandler = (function(my) { if (persistent) { args.closeText = ''; } - return $.prompt(msgString, args); + return new Impromptu(msgString, args); }; /** @@ -108,16 +108,10 @@ var messageHandler = (function(my) { * Shows a dialog with different states to the user. * * @param statesObject object containing all the states of the dialog - * @param loadedFunction function to be called after the prompt is fully loaded - * @param stateChangedFunction function to be called when the state of the dialog is changed */ - my.openDialogWithStates = function(statesObject, loadedFunction, stateChangedFunction) { + my.openDialogWithStates = function (statesObject, options) { - - var myPrompt = $.prompt(statesObject); - - myPrompt.on('impromptu:loaded', loadedFunction); - myPrompt.on('impromptu:statechanged', stateChangedFunction); + return new Impromptu(statesObject, options); }; /** diff --git a/modules/xmpp/moderator.js b/modules/xmpp/moderator.js index 6050cdffe..fc00fae31 100644 --- a/modules/xmpp/moderator.js +++ b/modules/xmpp/moderator.js @@ -188,6 +188,14 @@ var Moderator = { return elem; }, + parseSessionId: function (resultIq) { + var sessionId = $(resultIq).find('conference').attr('session-id'); + if (sessionId) { + console.info('Received sessionId: ' + sessionId); + localStorage.setItem('sessionId', sessionId); + } + }, + parseConfigOptions: function (resultIq) { Moderator.setFocusUserJid( @@ -209,12 +217,7 @@ var Moderator = { if (!externalAuthEnabled) { // We expect to receive sessionId in 'internal' authentication mode - var sessionId - = $(resultIq).find('conference').attr('session-id'); - if (sessionId) { - console.info('Received sessionId: ' + sessionId); - localStorage.setItem('sessionId', sessionId); - } + Moderator.parseSessionId(resultIq); } var authIdentity = $(resultIq).find('>conference').attr('identity'); @@ -295,22 +298,12 @@ var Moderator = { // Not authorized to create new room if ($(error).find('>error>not-authorized').length) { console.warn("Unauthorized to start the conference", error); - var toDomain - = Strophe.getDomainFromJid(error.getAttribute('to')); - if (toDomain === config.hosts.anonymousdomain) { - // we are connected with anonymous domain and - // only non anonymous users can create rooms - // we must authorize the user - self.xmppService.promptLogin(); - } else { - // External authentication mode - eventEmitter.emit( - XMPPEvents.AUTHENTICATION_REQUIRED, - function () { - Moderator.allocateConferenceFocus( - roomName, callback); - }); - } + eventEmitter.emit( + XMPPEvents.AUTHENTICATION_REQUIRED, + function () { + Moderator.allocateConferenceFocus( + roomName, callback); + }); return; } var waitMs = getNextErrorTimeout(); diff --git a/modules/xmpp/xmpp.js b/modules/xmpp/xmpp.js index 0db29858d..2a93b6062 100644 --- a/modules/xmpp/xmpp.js +++ b/modules/xmpp/xmpp.js @@ -13,8 +13,7 @@ var connection = null; var authenticatedUser = false; function connect(jid, password) { - var bosh = config.bosh || '/http-bind'; - connection = new Strophe.Connection(bosh); + connection = XMPP.createConnection(); Moderator.setConnection(connection); if (connection.disco) { @@ -127,6 +126,12 @@ function setupEvents() { var XMPP = { sessionTerminated: false, + + /** + * XMPP connection status + */ + Status: Strophe.Status, + /** * Remembers if we were muted by the focus. * @type {boolean} @@ -146,7 +151,16 @@ var XMPP = { 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 () { + // FIXME: re-use LoginDialog which supports retries APP.UI.showLoginPopup(connect); }, joinRoom: function(roomName, useNicks, nick)