/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.util.account; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.globalstatus.*; import net.java.sip.communicator.util.*; import org.osgi.framework.*; /** * The <tt>LoginManager</tt> manages the login operation. Here we obtain the * <tt>ProtocolProviderFactory</tt>, we make the account installation and we * handle all events related to the registration state. * <p> * The <tt>LoginManager</tt> is the one that opens one or more * <tt>LoginWindow</tt>s for each <tt>ProtocolProviderFactory</tt>. The * <tt>LoginWindow</tt> is where user could enter an identifier and password. * <p> * Note that the behavior of this class will be changed when the Configuration * Service is ready. * * @author Yana Stamcheva */ public class LoginManager implements ServiceListener, RegistrationStateChangeListener, AccountManagerListener { private static final Logger logger = Logger.getLogger(LoginManager.class); private boolean manuallyDisconnected = false; private final LoginRenderer loginRenderer; /** * Creates an instance of the <tt>LoginManager</tt>, by specifying the main * application window. * * @param loginRenderer the main application window */ public LoginManager(LoginRenderer loginRenderer) { this.loginRenderer = loginRenderer; UtilActivator.bundleContext.addServiceListener(this); } /** * Registers the given protocol provider. * * @param protocolProvider the ProtocolProviderService to register. */ public void login(ProtocolProviderService protocolProvider) { loginRenderer.startConnectingUI(protocolProvider); new RegisterProvider(protocolProvider, loginRenderer.getSecurityAuthorityImpl(protocolProvider)).start(); } /** * Unregisters the given protocol provider. * * @param protocolProvider the ProtocolProviderService to unregister */ public static void logoff(ProtocolProviderService protocolProvider) { new UnregisterProvider(protocolProvider).start(); } /** * Shows login window for each registered account. */ public void runLogin() { // if someone is late registering catch it UtilActivator.getAccountManager().addListener(this); for (ProtocolProviderFactory providerFactory : UtilActivator .getProtocolProviderFactories().values()) { addAccountsForProtocolProviderFactory(providerFactory); } } /** * Notifies that the loading of the stored accounts of a * specific <code>ProtocolProviderFactory</code> has finished. * * @param event the <code>AccountManagerEvent</code> describing the * <code>AccountManager</code> firing the notification and the * other details of the specific notification. */ public void handleAccountManagerEvent(AccountManagerEvent event) { if(event.getType() == AccountManagerEvent.STORED_ACCOUNTS_LOADED) { addAccountsForProtocolProviderFactory(event.getFactory()); } } /** * Handles stored accounts for a protocol provider factory and add them * to the UI and register them if needed. * @param providerFactory the factory to handle. */ private void addAccountsForProtocolProviderFactory( ProtocolProviderFactory providerFactory) { for (AccountID accountID : providerFactory.getRegisteredAccounts()) { ServiceReference<ProtocolProviderService> serRef = providerFactory.getProviderForAccount(accountID); ProtocolProviderService protocolProvider = UtilActivator.bundleContext.getService(serRef); handleProviderAdded(protocolProvider); } } /** * The method is called by a ProtocolProvider implementation whenever a * change in the registration state of the corresponding provider had * occurred. * * @param evt ProviderStatusChangeEvent the event describing the status * change. */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { RegistrationState newState = evt.getNewState(); ProtocolProviderService protocolProvider = evt.getProvider(); AccountID accountID = protocolProvider.getAccountID(); if (logger.isTraceEnabled()) logger.trace("Protocol provider: " + protocolProvider + " changed its state to: " + evt.getNewState().getStateName()); if (newState.equals(RegistrationState.REGISTERED) || newState.equals(RegistrationState.UNREGISTERED) || newState.equals(RegistrationState.EXPIRED) || newState.equals(RegistrationState.AUTHENTICATION_FAILED) || newState.equals(RegistrationState.CONNECTION_FAILED) || newState.equals(RegistrationState.CHALLENGED_FOR_AUTHENTICATION) || newState.equals(RegistrationState.REGISTERED)) { loginRenderer.stopConnectingUI(protocolProvider); } if (newState.equals(RegistrationState.REGISTERED)) { loginRenderer.protocolProviderConnected(protocolProvider, System.currentTimeMillis()); } else { String msgText; if (newState.equals(RegistrationState.AUTHENTICATION_FAILED)) { switch (evt.getReasonCode()) { case RegistrationStateChangeEvent .REASON_RECONNECTION_RATE_LIMIT_EXCEEDED: msgText = UtilActivator.getResources().getI18NString( "service.gui.RECONNECTION_LIMIT_EXCEEDED", new String[] { accountID.getUserID(), accountID.getService() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); break; case RegistrationStateChangeEvent.REASON_NON_EXISTING_USER_ID: msgText = UtilActivator.getResources().getI18NString( "service.gui.NON_EXISTING_USER_ID", new String[] { protocolProvider.getProtocolDisplayName() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); break; case RegistrationStateChangeEvent.REASON_TLS_REQUIRED: msgText = UtilActivator.getResources().getI18NString( "service.gui.NON_SECURE_CONNECTION", new String[] { accountID.getAccountAddress() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); break; default: break; } if (logger.isTraceEnabled()) logger.trace(evt.getReason()); } // CONNECTION_FAILED events are now dispatched in reconnect plugin // else if (newState.equals(RegistrationState.CONNECTION_FAILED)) // { // loginRenderer.protocolProviderConnectionFailed( // protocolProvider, // this); // // logger.trace(evt.getReason()); // } else if (newState.equals(RegistrationState.EXPIRED)) { msgText = UtilActivator.getResources().getI18NString( "service.gui.CONNECTION_EXPIRED_MSG", new String[] { protocolProvider.getProtocolDisplayName() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); logger.error(evt.getReason()); } else if (newState.equals(RegistrationState.UNREGISTERED)) { if (!manuallyDisconnected) { if (evt.getReasonCode() == RegistrationStateChangeEvent .REASON_MULTIPLE_LOGINS) { msgText = UtilActivator.getResources().getI18NString( "service.gui.MULTIPLE_LOGINS", new String[] { accountID.getUserID(), accountID.getService() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); } else if (evt.getReasonCode() == RegistrationStateChangeEvent .REASON_CLIENT_LIMIT_REACHED_FOR_IP) { msgText = UtilActivator.getResources().getI18NString( "service.gui.LIMIT_REACHED_FOR_IP", new String[] { protocolProvider.getProtocolDisplayName() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); } else if (evt.getReasonCode() == RegistrationStateChangeEvent .REASON_USER_REQUEST) { // do nothing } else { msgText = UtilActivator.getResources().getI18NString( "service.gui.UNREGISTERED_MESSAGE", new String[] { accountID.getUserID(), accountID.getService() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), msgText); } if (logger.isTraceEnabled()) logger.trace(evt.getReason()); } } } } /** * Implements the <tt>ServiceListener</tt> method. Verifies whether the * passed event concerns a <tt>ProtocolProviderService</tt> and adds the * corresponding UI controls. * * @param event The <tt>ServiceEvent</tt> object. */ public void serviceChanged(ServiceEvent event) { ServiceReference<?> serviceRef = event.getServiceReference(); // if the event is caused by a bundle being stopped, we don't want to // know if (serviceRef.getBundle().getState() == Bundle.STOPPING) return; Object service = UtilActivator.bundleContext.getService(serviceRef); // we don't care if the source service is not a protocol provider if (!(service instanceof ProtocolProviderService)) return; switch (event.getType()) { case ServiceEvent.REGISTERED: handleProviderAdded((ProtocolProviderService) service); break; case ServiceEvent.UNREGISTERING: handleProviderRemoved((ProtocolProviderService) service); break; } } /** * Adds all UI components (status selector box, etc) related to the given * protocol provider. * * @param protocolProvider the <tt>ProtocolProviderService</tt> */ private void handleProviderAdded(ProtocolProviderService protocolProvider) { if (logger.isTraceEnabled()) logger.trace("The following protocol provider was just added: " + protocolProvider.getAccountID().getAccountAddress()); synchronized(loginRenderer) { if(!loginRenderer.containsProtocolProviderUI(protocolProvider)) { protocolProvider.addRegistrationStateChangeListener(this); loginRenderer.addProtocolProviderUI(protocolProvider); } // we have already added this provider and scheduled // a login if needed // we've done our work, if it fails or something else // reconnect or other plugins will take care else return; } Object status = AccountStatusUtils .getProtocolProviderLastStatus(protocolProvider); if (status == null || status.equals(GlobalStatusEnum.ONLINE_STATUS) || ((status instanceof PresenceStatus) && (((PresenceStatus) status) .getStatus() >= PresenceStatus.ONLINE_THRESHOLD))) { login(protocolProvider); } } /** * Removes all UI components related to the given protocol provider. * * @param protocolProvider the <tt>ProtocolProviderService</tt> */ private void handleProviderRemoved(ProtocolProviderService protocolProvider) { loginRenderer.removeProtocolProviderUI(protocolProvider); } /** * Returns <tt>true</tt> to indicate the jitsi has been manually * disconnected, <tt>false</tt> - otherwise. * * @return <tt>true</tt> to indicate the jitsi has been manually * disconnected, <tt>false</tt> - otherwise */ public boolean isManuallyDisconnected() { return manuallyDisconnected; } /** * Sets the manually disconnected property. * * @param manuallyDisconnected <tt>true</tt> to indicate the jitsi has been * manually disconnected, <tt>false</tt> - otherwise */ public void setManuallyDisconnected(boolean manuallyDisconnected) { this.manuallyDisconnected = manuallyDisconnected; } /** * Registers a protocol provider in a separate thread. */ private class RegisterProvider extends Thread { private final ProtocolProviderService protocolProvider; private final SecurityAuthority secAuth; RegisterProvider( ProtocolProviderService protocolProvider, SecurityAuthority secAuth) { this.protocolProvider = protocolProvider; this.secAuth = secAuth; if(logger.isTraceEnabled()) logger.trace("Registering provider: " + protocolProvider.getAccountID().getAccountAddress(), new Exception( "Just tracing, provider registering, not an error!")); } /** * Registers the contained protocol provider and process all possible * errors that may occur during the registration process. */ @Override public void run() { try { protocolProvider.register(secAuth); } catch (OperationFailedException ex) { handleOperationFailedException(ex); } catch (Throwable ex) { logger.error("Failed to register protocol provider. ", ex); AccountID accountID = protocolProvider.getAccountID(); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), UtilActivator.getResources() .getI18NString("service.gui.LOGIN_GENERAL_ERROR", new String[] { accountID.getUserID(), accountID.getProtocolName(), accountID.getService() })); } } private void handleOperationFailedException(OperationFailedException ex) { String errorMessage = ""; switch (ex.getErrorCode()) { case OperationFailedException.GENERAL_ERROR: { logger.error("Provider could not be registered" + " due to the following general error: ", ex); AccountID accountID = protocolProvider.getAccountID(); errorMessage = UtilActivator.getResources().getI18NString( "service.gui.LOGIN_GENERAL_ERROR", new String[] { accountID.getUserID(), accountID.getProtocolName(), accountID.getService() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), errorMessage, ex); } break; case OperationFailedException.INTERNAL_ERROR: { logger.error("Provider could not be registered" + " due to the following internal error: ", ex); AccountID accountID = protocolProvider.getAccountID(); errorMessage = UtilActivator.getResources().getI18NString( "service.gui.LOGIN_INTERNAL_ERROR", new String[] { accountID.getUserID(), accountID.getService() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources().getI18NString( "service.gui.ERROR"), errorMessage, ex); } break; case OperationFailedException.NETWORK_FAILURE: { if (logger.isInfoEnabled()) { logger.info("Provider could not be registered" + " due to a network failure: " + ex); } loginRenderer.protocolProviderConnectionFailed( protocolProvider, LoginManager.this); } break; case OperationFailedException.INVALID_ACCOUNT_PROPERTIES: { logger.error("Provider could not be registered" + " due to an invalid account property: ", ex); AccountID accountID = protocolProvider.getAccountID(); errorMessage = UtilActivator.getResources().getI18NString( "service.gui.LOGIN_INVALID_PROPERTIES_ERROR", new String[] { accountID.getUserID(), accountID.getService() }); UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), errorMessage, ex); } break; default: logger.error("Provider could not be registered.", ex); } } } /** * Unregisters a protocol provider in a separate thread. */ private static class UnregisterProvider extends Thread { ProtocolProviderService protocolProvider; UnregisterProvider(ProtocolProviderService protocolProvider) { this.protocolProvider = protocolProvider; } /** * Unregisters the contained protocol provider and process all possible * errors that may occur during the un-registration process. */ @Override public void run() { try { protocolProvider.unregister(true); } catch (OperationFailedException ex) { int errorCode = ex.getErrorCode(); if (errorCode == OperationFailedException.GENERAL_ERROR) { logger.error("Provider could not be unregistered" + " due to the following general error: " + ex); } else if (errorCode == OperationFailedException.INTERNAL_ERROR) { logger.error("Provider could not be unregistered" + " due to the following internal error: " + ex); } else if (errorCode == OperationFailedException.NETWORK_FAILURE) { logger.error("Provider could not be unregistered" + " due to a network failure: " + ex); } UtilActivator.getAlertUIService().showAlertDialog( UtilActivator.getResources() .getI18NString("service.gui.ERROR"), UtilActivator.getResources() .getI18NString("service.gui.LOGOFF_NOT_SUCCEEDED", new String[] { protocolProvider.getAccountID().getUserID(), protocolProvider.getAccountID().getService() })); } } } }