gt:         boolean tlsRequired = loginStrategy.isTlsRequired();
context: /*
   * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package net.java.sip.communicator.impl.protocol.jabber;
  
  import java.math.*;
  import java.net.*;
  import java.security.*;
  import java.security.cert.*;
  import java.text.*;
  import java.util.*;
  
  import javax.net.ssl.*;
  
  import net.java.sip.communicator.impl.protocol.jabber.debugger.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.carbon.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.keepalive.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.messagecorrection.*;
  import net.java.sip.communicator.impl.protocol.jabber.extensions.version.*;
  import net.java.sip.communicator.service.certificate.*;
  import net.java.sip.communicator.service.dns.*;
  import net.java.sip.communicator.service.protocol.*;
  import net.java.sip.communicator.service.protocol.event.*;
  import net.java.sip.communicator.service.protocol.jabberconstants.*;
  import net.java.sip.communicator.util.*;
  import net.java.sip.communicator.util.Logger;
  
  import org.jitsi.service.configuration.*;
  import org.jitsi.service.neomedia.*;
  import org.jitsi.util.*;
  import org.jivesoftware.smack.*;
  import org.jivesoftware.smack.packet.*;
  import org.jivesoftware.smack.provider.*;
  import org.jivesoftware.smack.util.*;
  import org.jivesoftware.smack.util.StringUtils;
  import org.jivesoftware.smackx.*;
  import org.jivesoftware.smackx.packet.*;
  import org.xmlpull.v1.*;
  import org.xmpp.jnodes.smack.*;
  
  /**
   * An implementation of the protocol provider service over the Jabber protocol
   *
   * @author Damian Minkov
   * @author Symphorien Wanko
   * @author Lyubomir Marinov
   * @author Yana Stamcheva
   * @author Emil Ivov
   * @author Hristo Terezov
   */
  public class ProtocolProviderServiceJabberImpl
      extends AbstractProtocolProviderService
  {
      /**
       * Logger of this class
       */
      private static final Logger logger =
          Logger.getLogger(ProtocolProviderServiceJabberImpl.class);
  
      /**
       * Jingle's Discovery Info common URN.
       */
      public static final String URN_XMPP_JINGLE = JingleIQ.NAMESPACE;
  
      /**
       * Jingle's Discovery Info URN for RTP support.
       */
      public static final String URN_XMPP_JINGLE_RTP
          = RtpDescriptionPacketExtension.NAMESPACE;
  
      /**
       * Jingle's Discovery Info URN for RTP support with audio.
       */
      public static final String URN_XMPP_JINGLE_RTP_AUDIO
          = "urn:xmpp:jingle:apps:rtp:audio";
  
      /**
       * Jingle's Discovery Info URN for RTP support with video.
       */
      public static final String URN_XMPP_JINGLE_RTP_VIDEO
          = "urn:xmpp:jingle:apps:rtp:video";
  
      /**
       * Jingle's Discovery Info URN for ZRTP support with RTP.
       */
      public static final String URN_XMPP_JINGLE_RTP_ZRTP
          = ZrtpHashPacketExtension.NAMESPACE;
  
      /**
       * Jingle's Discovery Info URN for ICE_UDP transport support.
       */
      public static final String URN_XMPP_JINGLE_RAW_UDP_0
          = RawUdpTransportPacketExtension.NAMESPACE;
  
      /**
       * Jingle's Discovery Info URN for ICE_UDP transport support.
       */
      public static final String URN_XMPP_JINGLE_ICE_UDP_1
          = IceUdpTransportPacketExtension.NAMESPACE;
  
      /**
       * Jingle's Discovery Info URN for Jingle Nodes support.
       */
      public static final String URN_XMPP_JINGLE_NODES
          = "http://jabber.org/protocol/jinglenodes";
  
      /**
       * Jingle's Discovery Info URN for "XEP-0251: Jingle Session Transfer"
       * support.
       */
      public static final String URN_XMPP_JINGLE_TRANSFER_0
          = TransferPacketExtension.NAMESPACE;
  
      /**
       * Jingle's Discovery Info URN for "XEP-298 :Delivering Conference
       * Information to Jingle Participants (Coin)" support.
       */
      public static final String URN_XMPP_JINGLE_COIN = "urn:xmpp:coin";
  
      /**
       * Jingle's Discovery Info URN for &quot;XEP-0320: Use of DTLS-SRTP in
       * Jingle Sessions&quot;.
       */
      public static final String URN_XMPP_JINGLE_DTLS_SRTP
          = "urn:xmpp:jingle:apps:dtls:0";
  
      /**
       * Discovery Info URN for classic RFC3264-style Offer/Answer negotiation
       * with no support for Trickle ICE and low tolerance to transport/payload
       * separation. Defined in XEP-0176
       */
      public static final String URN_IETF_RFC_3264 = "urn:ietf:rfc:3264";
  
      /**
       * Jingle's Discovery Info URN for "XEP-0294: Jingle RTP Header Extensions
       * Negotiation" support.
       */
      public static final String URN_XMPP_JINGLE_RTP_HDREXT =
          "urn:xmpp:jingle:apps:rtp:rtp-hdrext:0";
  
      /**
       * URN for XEP-0077 inband registration
       */
      public static final String URN_REGISTER = "jabber:iq:register";
  
      /**
       * The name of the property under which the user may specify if the desktop
       * streaming or sharing should be disabled.
       */
      private static final String IS_DESKTOP_STREAMING_DISABLED
          = "net.java.sip.communicator.impl.protocol.jabber." +
              "DESKTOP_STREAMING_DISABLED";
  
      /**
       * The name of the property under which the user may specify if audio/video
       * calls should be disabled.
       */
      private static final String IS_CALLING_DISABLED
          = "net.java.sip.communicator.impl.protocol.jabber.CALLING_DISABLED";
  
      /**
       * Smack packet reply timeout.
       */
      public static final int SMACK_PACKET_REPLY_TIMEOUT = 45000;
  
      /**
       * Property for vcard reply timeout. Time to wait before
       * we think vcard retrieving has timeouted, default value
       * of smack is 5000 (5 sec.).
       */
      public static final String VCARD_REPLY_TIMEOUT_PROPERTY =
          "net.java.sip.communicator.impl.protocol.jabber.VCARD_REPLY_TIMEOUT";
  
      /**
       * XMPP signaling DSCP configuration property name.
       */
      private static final String XMPP_DSCP_PROPERTY =
          "net.java.sip.communicator.impl.protocol.XMPP_DSCP";
  
      /**
       * Indicates if user search is disabled.
       */
      private static final String IS_USER_SEARCH_ENABLED_PROPERTY
          = "USER_SEARCH_ENABLED";
  
      /**
       * Google voice domain name.
       */
      public static final String GOOGLE_VOICE_DOMAIN = "voice.google.com";
  
      /**
       * Used to connect to a XMPP server.
       */
      private XMPPConnection connection;
  
      /**
       * The socket address of the XMPP server.
       */
      private InetSocketAddress address;
  
      /**
       * Indicates whether or not the provider is initialized and ready for use.
       */
      private boolean isInitialized = false;
  
      /**
       * We use this to lock access to initialization.
       */
      private final Object initializationLock = new Object();
  
      /**
       * The identifier of the account that this provider represents.
       */
      private AccountID accountID = null;
  
      /**
       * Used when we need to re-register
       */
      private SecurityAuthority authority = null;
  
      /**
       * The resource we will use when connecting during this run.
       */
      private String resource = null;
  
      /**
       * The icon corresponding to the jabber protocol.
       */
      private ProtocolIconJabberImpl jabberIcon;
  
      /**
       * A set of features supported by our Jabber implementation.
       * In general, we add new feature(s) when we add new operation sets.
       * (see xep-0030 : http://www.xmpp.org/extensions/xep-0030.html#info).
       * Example : to tell the world that we support jingle, we simply have
       * to do :
       * supportedFeatures.add("http://www.xmpp.org/extensions/xep-0166.html#ns");
       * Beware there is no canonical mapping between op set and jabber features
       * (op set is a SC "concept"). This means that one op set in SC can
       * correspond to many jabber features. It is also possible that there is no
       * jabber feature corresponding to a SC op set or again,
       * we can currently support some features wich do not have a specific
       * op set in SC (the mandatory feature :
       * http://jabber.org/protocol/disco#info is one example).
       * We can find features corresponding to op set in the xep(s) related
       * to implemented functionality.
       */
      private final List<String> supportedFeatures = new ArrayList<String>();
  
      /**
       * The <tt>ServiceDiscoveryManager</tt> is responsible for advertising
       * <tt>supportedFeatures</tt> when asked by a remote client. It can also
       * be used to query remote clients for supported features.
       */
      private ScServiceDiscoveryManager discoveryManager = null;
  
      /**
       * The <tt>OperationSetContactCapabilities</tt> of this
       * <tt>ProtocolProviderService</tt> which is the service-public counterpart
       * of {@link #discoveryManager}.
       */
      private OperationSetContactCapabilitiesJabberImpl opsetContactCapabilities;
  
      /**
       * The statuses.
       */
      private JabberStatusEnum jabberStatusEnum;
  
      /**
       * The service we use to interact with user.
       */
      private CertificateService guiVerification;
  
      /**
       * Used with tls connecting when certificates are not trusted
       * and we ask the user to confirm connection. When some timeout expires
       * connect method returns, and we use abortConnecting to abort further
       * execution cause after user chooses we make further processing from there.
       */
      private boolean abortConnecting = false;
  
      /**
       * Flag indicating are we currently executing connectAndLogin method.
       */
      private boolean inConnectAndLogin = false;
  
      /**
       * Object used to synchronize the flag inConnectAndLogin.
       */
      private final Object connectAndLoginLock = new Object();
  
      /**
       * If an event occurs during login we fire it at the end of the login
       * process (at the end of connectAndLogin method).
       */
      private RegistrationStateChangeEvent eventDuringLogin;
  
      /**
       * Listens for connection closes or errors.
       */
      private JabberConnectionListener connectionListener;
  
      /**
       * The details of the proxy we are using to connect to the server (if any)
       */
      private org.jivesoftware.smack.proxy.ProxyInfo proxy;
  
      /**
       * Our provider manager instances.
       */
      private static ProviderManager providerManager = null;
  
      /**
       * Lock for creating provider.
       */
      private static Object providerCreationLock = new Object();
  
      /**
       * State for connect and login state.
       */
      enum ConnectState
      {
          /**
           * Abort any further connecting.
           */
          ABORT_CONNECTING,
          /**
           * Continue trying with next address.
           */
          CONTINUE_TRYING,
          /**
           * Stop trying we succeeded or just have a final state for
           * the whole connecting procedure.
           */
          STOP_TRYING
      }
  
      /**
       * The debugger who logs packets.
       */
      private SmackPacketDebugger debugger = null;
  
      /**
       * Jingle Nodes service.
       */
      private SmackServiceNode jingleNodesServiceNode = null;
  
      /**
       * Synchronization object to monitore jingle nodes auto discovery.
       */
      private final Object jingleNodesSyncRoot = new Object();
  
      /**
       * Stores user credentials for local use if user hasn't stored
       * its password.
       */
      private UserCredentials userCredentials = null;
  
      /**
       * The currently running keepAliveManager if enabled.
       */
      private KeepAliveManager keepAliveManager = null;
  
      /**
       * The version manager.
       */
      private VersionManager versionManager = null;
  
      // load xmpp manager classes
      static
      {
          if(OSUtils.IS_ANDROID)
              loadJabberServiceClasses();
      }
  
      /**
       * An <tt>OperationSet</tt> that allows access to connection information used
       * by the protocol provider.
       */
      private class OperationSetConnectionInfoJabberImpl
         implements OperationSetConnectionInfo
      {
         /**
          * @return The XMPP server address.
          */
          @Override
          public InetSocketAddress getServerAddress()
          {
              return address;
          }
      }
  
      /**
       * Returns the state of the registration of this protocol provider
       * @return the <tt>RegistrationState</tt> that this provider is
       * currently in or null in case it is in a unknown state.
       */
      public RegistrationState getRegistrationState()
      {
          if(connection == null)
              return RegistrationState.UNREGISTERED;
          else if(connection.isConnected() && connection.isAuthenticated())
              return RegistrationState.REGISTERED;
          else
              return RegistrationState.UNREGISTERED;
      }
  
      /**
       * Return the certificate verification service impl.
       * @return the CertificateVerification service.
       */
      private CertificateService getCertificateVerificationService()
      {
          if(guiVerification == null)
          {
              guiVerification
                  = ServiceUtils.getService(
                          JabberActivator.getBundleContext(),
                          CertificateService.class);
          }
  
          return guiVerification;
      }
  
      /**
       * Starts the registration process. Connection details such as
       * registration server, user name/number are provided through the
       * configuration service through implementation specific properties.
       *
       * @param authority the security authority that will be used for resolving
       *        any security challenges that may be returned during the
       *        registration or at any moment while we're registered.
       * @throws OperationFailedException with the corresponding code it the
       * registration fails for some reason (e.g. a networking error or an
       * implementation problem).
       */
      public void register(final SecurityAuthority authority)
          throws OperationFailedException
      {
          if(authority == null)
              throw new IllegalArgumentException(
                  "The register method needs a valid non-null authority impl "
                  + " in order to be able and retrieve passwords.");
  
          this.authority = authority;
  
          try
          {
              // reset states
              abortConnecting = false;
  
              // indicate we started connectAndLogin process
              synchronized(connectAndLoginLock)
              {
                  inConnectAndLogin = true;
              }
  
              initializeConnectAndLogin(authority,
                  SecurityAuthority.AUTHENTICATION_REQUIRED);
          }
          catch (XMPPException ex)
          {
              logger.error("Error registering", ex);
  
              eventDuringLogin = null;
  
              fireRegistrationStateChanged(ex);
          }
          finally
          {
              synchronized(connectAndLoginLock)
              {
                  // Checks if an error has occurred during login, if so we fire
                  // it here in order to avoid a deadlock which occurs in
                  // reconnect plugin. The deadlock is cause we fired an event
                  // during login process and have locked initializationLock and
                  // we cannot unregister from reconnect, cause unregister method
                  // also needs this lock.
                  if(eventDuringLogin != null)
                  {
                      if(eventDuringLogin.getNewState().equals(
                              RegistrationState.CONNECTION_FAILED) ||
                          eventDuringLogin.getNewState().equals(
                              RegistrationState.UNREGISTERED))
                          disconnectAndCleanConnection();
  
                      fireRegistrationStateChanged(
                          eventDuringLogin.getOldState(),
                          eventDuringLogin.getNewState(),
                          eventDuringLogin.getReasonCode(),
                          eventDuringLogin.getReason());
  
                      eventDuringLogin = null;
                      inConnectAndLogin = false;
                      return;
                  }
  
                  inConnectAndLogin = false;
              }
          }
      }
  
      /**
       * Connects and logins again to the server.
       *
       * @param authReasonCode indicates the reason of the re-authentication.
       */
      void reregister(int authReasonCode)
      {
          try
          {
              if (logger.isTraceEnabled())
                  logger.trace("Trying to reregister us!");
  
              // sets this if any is trying to use us through registration
              // to know we are not registered
              this.unregisterInternal(false);
  
              // reset states
              this.abortConnecting = false;
  
              // indicate we started connectAndLogin process
              synchronized(connectAndLoginLock)
              {
                  inConnectAndLogin = true;
              }
  
              initializeConnectAndLogin(authority, authReasonCode);
          }
          catch(OperationFailedException ex)
          {
              logger.error("Error ReRegistering", ex);
  
              eventDuringLogin = null;
  
              disconnectAndCleanConnection();
  
              fireRegistrationStateChanged(getRegistrationState(),
                  RegistrationState.CONNECTION_FAILED,
                  RegistrationStateChangeEvent.REASON_INTERNAL_ERROR, null);
          }
          catch (XMPPException ex)
          {
              logger.error("Error ReRegistering", ex);
  
              eventDuringLogin = null;
  
              fireRegistrationStateChanged(ex);
          }
          finally
          {
              synchronized(connectAndLoginLock)
              {
                  // Checks if an error has occurred during login, if so we fire
                  // it here in order to avoid a deadlock which occurs in
                  // reconnect plugin. The deadlock is cause we fired an event
                  // during login process and have locked initializationLock and
                  // we cannot unregister from reconnect, cause unregister method
                  // also needs this lock.
                  if(eventDuringLogin != null)
                  {
                      if(eventDuringLogin.getNewState().equals(
                              RegistrationState.CONNECTION_FAILED) ||
                          eventDuringLogin.getNewState().equals(
                              RegistrationState.UNREGISTERED))
                          disconnectAndCleanConnection();
  
                      fireRegistrationStateChanged(
                          eventDuringLogin.getOldState(),
                          eventDuringLogin.getNewState(),
                          eventDuringLogin.getReasonCode(),
                          eventDuringLogin.getReason());
  
                      eventDuringLogin = null;
                      inConnectAndLogin = false;
                      return;
                  }
  
                  inConnectAndLogin = false;
              }
          }
      }
  
      /**
       * Indicates if the XMPP transport channel is using a TLS secured socket.
       *
       * @return True when TLS is used, false otherwise.
       */
      public boolean isSignalingTransportSecure()
      {
          return connection != null && connection.isUsingTLS();
      }
  
      /**
       * Returns the "transport" protocol of this instance used to carry the
       * control channel for the current protocol service.
       *
       * @return The "transport" protocol of this instance: TCP, TLS or UNKNOWN.
       */
      public TransportProtocol getTransportProtocol()
      {
          // Without a connection, there is no transport available.
          if(connection != null && connection.isConnected())
          {
              // Transport using a secure connection.
              if(connection.isUsingTLS())
              {
                  return TransportProtocol.TLS;
              }
              // Transport using a unsecure connection.
              return TransportProtocol.TCP;
          }
          return TransportProtocol.UNKNOWN;
      }
  
      /**
       * Connects and logins to the server
       * @param authority SecurityAuthority
       * @param reasonCode the authentication reason code. Indicates the reason of
       * this authentication.
       * @throws XMPPException if we cannot connect to the server - network problem
       * @throws  OperationFailedException if login parameters
       *          as server port are not correct
       */
      private void initializeConnectAndLogin(SecurityAuthority authority,
                                                int reasonCode)
          throws XMPPException, OperationFailedException
      {
          synchronized(initializationLock)
          {
              // if a thread is waiting for initializationLock and enters
              // lets check whether someone hasn't already tried login and
              // have succeeded,
              // should prevent "Trace possible duplicate connections" prints
              if(isRegistered())
                  return;
  
              JabberLoginStrategy loginStrategy = createLoginStrategy();
              userCredentials = loginStrategy.prepareLogin(authority, reasonCode);
              if(!loginStrategy.loginPreparationSuccessful())
                  return;
  
              String serviceName
                  = StringUtils.parseServer(getAccountID().getUserID());
  
              loadResource();
              loadProxy();
              Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual);
  
              ConnectState state;
              //[0] = hadDnsSecException
              boolean[] hadDnsSecException = new boolean[]{false};
  
              // try connecting with auto-detection if enabled
              boolean isServerOverriden =
                  getAccountID().getAccountPropertyBoolean(
                      ProtocolProviderFactory.IS_SERVER_OVERRIDDEN, false);
  
              if(!isServerOverriden)
              {
                  state = connectUsingSRVRecords(serviceName,
                          serviceName, hadDnsSecException, loginStrategy);
                  if(hadDnsSecException[0])
                  {
                      setDnssecLoginFailure();
                      return;
                  }
                  if(state == ConnectState.ABORT_CONNECTING
                      || state == ConnectState.STOP_TRYING)
                      return;
              }
  
              // check for custom xmpp domain which we will check for
              // SRV records for server addresses
              String customXMPPDomain = getAccountID()
                  .getAccountPropertyString("CUSTOM_XMPP_DOMAIN");
  
              if(customXMPPDomain != null && !hadDnsSecException[0])
              {
                  logger.info("Connect using custom xmpp domain: " +
                      customXMPPDomain);
  
                  state = connectUsingSRVRecords(
                              customXMPPDomain, serviceName,
                              hadDnsSecException, loginStrategy);
  
                  logger.info("state for connectUsingSRVRecords: " + state);
  
                  if(hadDnsSecException[0])
                  {
                      setDnssecLoginFailure();
                      return;
                  }
  
                  if(state == ConnectState.ABORT_CONNECTING
                      || state == ConnectState.STOP_TRYING)
                      return;
              }
  
              // connect with specified server name
              String serverAddressUserSetting
                  = getAccountID().getAccountPropertyString(
                      ProtocolProviderFactory.SERVER_ADDRESS);
  
              int serverPort = getAccountID().getAccountPropertyInt(
                      ProtocolProviderFactory.SERVER_PORT, 5222);
  
              InetSocketAddress[] addrs = null;
              try
              {
                  addrs = NetworkUtils.getAandAAAARecords(
                      serverAddressUserSetting,
                      serverPort
                  );
              }
              catch (ParseException e)
              {
                  logger.error("Domain not resolved", e);
              }
              catch (DnssecException e)
              {
                  logger.error("DNSSEC failure for overridden server", e);
                  setDnssecLoginFailure();
                  return;
              }
  
              if (addrs == null || addrs.length == 0)
              {
                  logger.error("No server addresses found");
  
                  eventDuringLogin = null;
  
                  fireRegistrationStateChanged(
                      getRegistrationState(),
                      RegistrationState.CONNECTION_FAILED,
                      RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND,
                      "No server addresses found");
              }
              else
              {
                  for (InetSocketAddress isa : addrs)
                  {
                      try
                      {
                          state = connectAndLogin(isa, serviceName,
                              loginStrategy);
                          if(state == ConnectState.ABORT_CONNECTING
                              || state == ConnectState.STOP_TRYING)
                              return;
                      }
                      catch(XMPPException ex)
                      {
                          disconnectAndCleanConnection();
                          if(isAuthenticationFailed(ex))
                              throw ex;
                      }
                  }
              }
          }
      }
  
      /**
       * Creates the JabberLoginStrategy to use for the current account.
       */
      private JabberLoginStrategy createLoginStrategy()
      {
          if (((JabberAccountIDImpl)getAccountID()).isAnonymousAuthUsed())
          {
              return new AnonymousLoginStrategy(
                  getAccountID().getAuthorizationName());
          }
  
          String clientCertId = getAccountID().getAccountPropertyString(
                  ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE);
          if(clientCertId != null)
          {
              return new LoginByClientCertificateStrategy(getAccountID());
          }
          else
          {
              return new LoginByPasswordStrategy(this, getAccountID());
          }
      }
  
      private void setDnssecLoginFailure()
      {
          eventDuringLogin = new RegistrationStateChangeEvent(
              this,
              getRegistrationState(),
              RegistrationState.UNREGISTERED,
              RegistrationStateChangeEvent.REASON_USER_REQUEST,
              "No usable host found due to DNSSEC failures");
      }
  
      /**
       * Connects using the domain specified and its SRV records.
       * @param domain the domain to use
       * @param serviceName the domain name of the user's login
       * @param dnssecState state of possible received DNSSEC exceptions
       * @param loginStrategy the login strategy to use
       * @return whether to continue trying or stop.
       */
      private ConnectState connectUsingSRVRecords(
          String domain,
          String serviceName,
          boolean[] dnssecState,
          JabberLoginStrategy loginStrategy)
          throws XMPPException
      {
          // check to see is there SRV records for this server domain
          SRVRecord srvRecords[] = null;
          try
          {
              srvRecords = NetworkUtils
                  .getSRVRecords("xmpp-client", "tcp", domain);
          }
          catch (ParseException e)
          {
              logger.error("SRV record not resolved", e);
          }
          catch (DnssecException e)
          {
              logger.error("DNSSEC failure for SRV lookup", e);
              dnssecState[0] = true;
          }
  
          if(srvRecords != null)
          {
              for(SRVRecord srv : srvRecords)
              {
                  InetSocketAddress[] addrs = null;
                  try
                  {
                      addrs =
                          NetworkUtils.getAandAAAARecords(
                              srv.getTarget(),
                              srv.getPort()
                          );
                  }
                  catch (ParseException e)
                  {
                      logger.error("Invalid SRV record target", e);
                  }
                  catch (DnssecException e)
                  {
                      logger.error("DNSSEC failure for A/AAAA lookup of SRV", e);
                      dnssecState[0] = true;
                  }
  
                  if (addrs == null || addrs.length == 0)
                  {
                      logger.error("No A/AAAA addresses found for " +
                          srv.getTarget());
                      continue;
                  }
  
                  for (InetSocketAddress isa : addrs)
                  {
                      try
                      {
                          // if failover mechanism is enabled, use it,
                          // default is not enabled.
                          if(JabberActivator.getConfigurationService()
                              .getBoolean(FailoverConnectionMonitor
                                                  .REVERSE_FAILOVER_ENABLED_PROP,
                                  false
                              ))
                          {
                              FailoverConnectionMonitor.getInstance(this)
                                  .setCurrent(domain, srv.getTarget());
                          }
  
                          ConnectState state = connectAndLogin(
                              isa, serviceName, loginStrategy);
                          return state;
                      }
                      catch(XMPPException ex)
                      {
                          logger.error("Error connecting to " + isa
                              + " for domain:" + domain
                              + " serviceName:" + serviceName, ex);
  
                          disconnectAndCleanConnection();
  
                          if(isAuthenticationFailed(ex))
                              throw ex;
                      }
                  }
              }
          }
          else
              logger.error("No SRV addresses found for _xmpp-client._tcp."
                  + domain);
  
          return ConnectState.CONTINUE_TRYING;
      }
  
      /**
       * Tries to login to the XMPP server with the supplied user ID. If the
       * protocol is Google Talk, the user ID including the service name is used.
       * For other protocols, if the login with the user ID without the service
       * name fails, a second attempt including the service name is made.
       *
       * @param currentAddress the IP address to connect to
       * @param serviceName the domain name of the user's login
       * @param loginStrategy the login strategy to use
       * @throws XMPPException when a failure occurs
       */
      private ConnectState connectAndLogin(InetSocketAddress currentAddress,
          String serviceName,
          JabberLoginStrategy loginStrategy)
          throws XMPPException
      {
          String userID = null;
          boolean qualifiedUserID;
  
          /* with a google account (either gmail or google apps
           * related ones), the userID MUST be the full e-mail address
           * not just the ID
           */
          if(getAccountID().getProtocolDisplayName().equals("Google Talk"))
          {
              userID = getAccountID().getUserID();
              qualifiedUserID = true;
          }
          else
          {
              userID = StringUtils.parseName(getAccountID().getUserID());
              qualifiedUserID = false;
          }
  
          try
          {
              return connectAndLogin(
                  currentAddress, serviceName,
                  userID, resource, loginStrategy);
          }
          catch(XMPPException ex)
          {
              // server disconnect us after such an error, do cleanup
              disconnectAndCleanConnection();
  
              //no need to check with a different username if the
              //socket could not be opened
              if (ex.getWrappedThrowable() instanceof ConnectException
                  || ex.getWrappedThrowable() instanceof NoRouteToHostException)
              {
                  //as we got an exception not handled in connectAndLogin
                  //no state was set, so fire it here so we can continue
                  //with the re-register process
                  //2013-08-07 do not fire event, if we have several
                  // addresses and we fire event will activate reconnect
                  // but we will continue connecting with other addresses
                  // and can register with address, then unregister and try again
                  // that is from reconnect plugin.
                  // Storing event for fire after all have failed and we have
                  // tried every address.
                  eventDuringLogin = new RegistrationStateChangeEvent(
                      ProtocolProviderServiceJabberImpl.this,
                      getRegistrationState(),
                      RegistrationState.CONNECTION_FAILED,
                      RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND,
                      null);
  
                  throw ex;
              }
  
              // don't attempt to append the service name if it's already there
              if (!qualifiedUserID)
              {
                  try
                  {
                      // logging in might need the service name
                      return connectAndLogin(
                          currentAddress, serviceName,
                          userID + "@" + serviceName,
                          resource,
                          loginStrategy);
                  }
                  catch(XMPPException ex2)
                  {
                      disconnectAndCleanConnection();
                      throw ex; //throw the original exception
                  }
              }
              else
                  throw ex;
          }
      }
  
      /**
       * Initializes the Jabber Resource identifier.
       */
      private void loadResource()
      {
          if(resource == null)
          {
              String defaultResource = "jitsi";
              String autoGenenerateResource =
                  getAccountID().getAccountPropertyString(
                      ProtocolProviderFactory.AUTO_GENERATE_RESOURCE);
              if(autoGenenerateResource == null ||
                  Boolean.parseBoolean(autoGenenerateResource))
              {
                  SecureRandom random = new SecureRandom();
  
                  resource = defaultResource + "-" +
                      new BigInteger(32, random).toString(32);
              }
              else
              {
                  resource = getAccountID().getAccountPropertyString(
                      ProtocolProviderFactory.RESOURCE);
  
                  if(resource == null || resource.length() == 0)
                      resource = defaultResource;
              }
          }
      }
  
      /**
       * Sets the global proxy information based on the configuration
       *
       * @throws OperationFailedException
       */
      private void loadProxy() throws OperationFailedException
      {
          String globalProxyType =
              JabberActivator.getConfigurationService()
              .getString(ProxyInfo.CONNECTION_PROXY_TYPE_PROPERTY_NAME);
          if(globalProxyType == null ||
             globalProxyType.equals(ProxyInfo.ProxyType.NONE.name()))
          {
              proxy = org.jivesoftware.smack.proxy.ProxyInfo.forNoProxy();
          }
          else
          {
              String globalProxyAddress =
                  JabberActivator.getConfigurationService().getString(
                  ProxyInfo.CONNECTION_PROXY_ADDRESS_PROPERTY_NAME);
              String globalProxyPortStr =
                  JabberActivator.getConfigurationService().getString(
                  ProxyInfo.CONNECTION_PROXY_PORT_PROPERTY_NAME);
              int globalProxyPort;
              try
              {
                  globalProxyPort = Integer.parseInt(
                      globalProxyPortStr);
              }
              catch(NumberFormatException ex)
              {
                  throw new OperationFailedException("Wrong proxy port, "
                          + globalProxyPortStr
                          + " does not represent an integer",
                      OperationFailedException.INVALID_ACCOUNT_PROPERTIES,
                      ex);
              }
              String globalProxyUsername =
                  JabberActivator.getConfigurationService().getString(
                  ProxyInfo.CONNECTION_PROXY_USERNAME_PROPERTY_NAME);
              String globalProxyPassword =
                  JabberActivator.getConfigurationService().getString(
                  ProxyInfo.CONNECTION_PROXY_PASSWORD_PROPERTY_NAME);
              if(globalProxyAddress == null ||
                  globalProxyAddress.length() <= 0)
              {
                  throw new OperationFailedException(
                      "Missing Proxy Address",
                      OperationFailedException.INVALID_ACCOUNT_PROPERTIES);
              }
              try
              {
                  proxy = new org.jivesoftware.smack.proxy.ProxyInfo(
                      Enum.valueOf(org.jivesoftware.smack.proxy.ProxyInfo.
                          ProxyType.class, globalProxyType),
                      globalProxyAddress, globalProxyPort,
                      globalProxyUsername, globalProxyPassword);
              }
              catch(IllegalArgumentException e)
              {
                  logger.error("Invalid value for smack proxy enum", e);
                  proxy = null;
              }
          }
      }
  
      /**
       * Connects xmpp connection and login. Returning the state whether is it
       * final - Abort due to certificate cancel or keep trying cause only current
       * address has failed or stop trying cause we succeeded.
       * @param address the address to connect to
       * @param serviceName the service name to use
       * @param userName the username to use
       * @param resource and the resource.
       * @param loginStrategy the login strategy to use
       * @return return the state how to continue the connect process.
       * @throws XMPPException if we cannot connect for some reason
       */
      private ConnectState connectAndLogin(
              InetSocketAddress address, String serviceName,
              String userName, String resource,
              JabberLoginStrategy loginStrategy)
          throws XMPPException
      {
          ConnectionConfiguration confConn = new ConnectionConfiguration(
                  address.getAddress().getHostAddress(),
                  address.getPort(),
                  serviceName, proxy
          );
  
          // if we have OperationSetPersistentPresence skip sending initial
          // presence while login is executed, the OperationSet will take care
          // of it
          if(getOperationSet(OperationSetPersistentPresence.class) != null)
              confConn.setSendPresence(false);
  
          confConn.setReconnectionAllowed(false);
  