package org.jivesoftware.smack;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.security.auth.callback.CallbackHandler;
import org.jivesoftware.smack.packet.Mechanisms;
import org.jivesoftware.smack.sasl.SASLAnonymous;
import org.jivesoftware.smack.sasl.SASLErrorException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jxmpp.jid.DomainBareJid;

public class SASLAuthentication {
   private static final Logger LOGGER = Logger.getLogger(SASLAuthentication.class.getName());
   private static final List<SASLMechanism> REGISTERED_MECHANISMS = new ArrayList();
   private static final Set<String> BLACKLISTED_MECHANISMS = new HashSet();
   private final AbstractXMPPConnection connection;
   private SASLMechanism currentMechanism = null;
   private boolean authenticationSuccessful;
   private Exception saslException;

   public static void registerSASLMechanism(SASLMechanism mechanism) {
      synchronized(REGISTERED_MECHANISMS) {
         REGISTERED_MECHANISMS.add(mechanism);
         Collections.sort(REGISTERED_MECHANISMS);
      }
   }

   public static Map<String, String> getRegisterdSASLMechanisms() {
      Map<String, String> answer = new HashMap();
      synchronized(REGISTERED_MECHANISMS) {
         Iterator i$ = REGISTERED_MECHANISMS.iterator();

         while(i$.hasNext()) {
            SASLMechanism mechanism = (SASLMechanism)i$.next();
            answer.put(mechanism.getClass().getName(), mechanism.getName());
         }

         return answer;
      }
   }

   public static boolean unregisterSASLMechanism(String clazz) {
      synchronized(REGISTERED_MECHANISMS) {
         Iterator it = REGISTERED_MECHANISMS.iterator();

         SASLMechanism mechanism;
         do {
            if (!it.hasNext()) {
               return false;
            }

            mechanism = (SASLMechanism)it.next();
         } while(!mechanism.getClass().getName().equals(clazz));

         it.remove();
         return true;
      }
   }

   public static boolean blacklistSASLMechanism(String mechansim) {
      synchronized(BLACKLISTED_MECHANISMS) {
         return BLACKLISTED_MECHANISMS.add(mechansim);
      }
   }

   public static boolean unBlacklistSASLMechanism(String mechanism) {
      synchronized(BLACKLISTED_MECHANISMS) {
         return BLACKLISTED_MECHANISMS.remove(mechanism);
      }
   }

   public static Set<String> getBlacklistedSASLMechanisms() {
      synchronized(BLACKLISTED_MECHANISMS) {
         return new HashSet(BLACKLISTED_MECHANISMS);
      }
   }

   SASLAuthentication(AbstractXMPPConnection connection) {
      this.connection = connection;
      this.init();
   }

   public boolean hasAnonymousAuthentication() {
      return this.serverMechanisms().contains("ANONYMOUS");
   }

   public boolean hasNonAnonymousAuthentication() {
      return !this.serverMechanisms().isEmpty() && (this.serverMechanisms().size() != 1 || !this.hasAnonymousAuthentication());
   }

   public void authenticate(String resource, CallbackHandler cbh) throws IOException, XMPPException.XMPPErrorException, SASLErrorException, SmackException, InterruptedException {
      SASLMechanism selectedMechanism = this.selectMechanism();
      if (selectedMechanism != null) {
         this.currentMechanism = selectedMechanism;
         synchronized(this) {
            this.currentMechanism.authenticate(this.connection.getHost(), this.connection.getServiceName(), cbh);
            this.wait(this.connection.getPacketReplyTimeout());
         }

         this.maybeThrowException();
         if (!this.authenticationSuccessful) {
            throw SmackException.NoResponseException.newWith(this.connection);
         }
      } else {
         throw new SmackException("SASL Authentication failed. No known authentication mechanisims.");
      }
   }

   public void authenticate(String username, String password, String resource) throws XMPPException.XMPPErrorException, SASLErrorException, IOException, SmackException, InterruptedException {
      SASLMechanism selectedMechanism = this.selectMechanism();
      if (selectedMechanism != null) {
         this.currentMechanism = selectedMechanism;
         synchronized(this) {
            this.currentMechanism.authenticate(username, this.connection.getHost(), this.connection.getServiceName(), password);
            this.wait(this.connection.getPacketReplyTimeout());
         }

         this.maybeThrowException();
         if (!this.authenticationSuccessful) {
            throw SmackException.NoResponseException.newWith(this.connection);
         }
      } else {
         throw new SmackException("SASL Authentication failed. No known authentication mechanisims.");
      }
   }

   public void authenticateAnonymously() throws SASLErrorException, SmackException, XMPPException.XMPPErrorException, InterruptedException {
      this.currentMechanism = (new SASLAnonymous()).instanceForAuthentication(this.connection);
      synchronized(this) {
         this.currentMechanism.authenticate((String)null, (String)null, (DomainBareJid)null, "");
         this.wait(this.connection.getPacketReplyTimeout());
      }

      this.maybeThrowException();
      if (!this.authenticationSuccessful) {
         throw SmackException.NoResponseException.newWith(this.connection);
      }
   }

   private void maybeThrowException() throws SmackException, SASLErrorException {
      if (this.saslException != null) {
         if (this.saslException instanceof SmackException) {
            throw (SmackException)this.saslException;
         } else if (this.saslException instanceof SASLErrorException) {
            throw (SASLErrorException)this.saslException;
         } else {
            throw new IllegalStateException("Unexpected exception type", this.saslException);
         }
      }
   }

   public void challengeReceived(String challenge) throws SmackException, InterruptedException {
      this.challengeReceived(challenge, false);
   }

   public void challengeReceived(String challenge, boolean finalChallenge) throws SmackException, InterruptedException {
      try {
         this.currentMechanism.challengeReceived(challenge, finalChallenge);
      } catch (SmackException | InterruptedException var4) {
         this.authenticationFailed((Exception)var4);
         throw var4;
      }
   }

   public void authenticated(SaslStreamElements.Success success) throws SmackException, InterruptedException {
      if (success.getData() != null) {
         this.challengeReceived(success.getData(), true);
      }

      this.currentMechanism.checkIfSuccessfulOrThrow();
      this.authenticationSuccessful = true;
      synchronized(this) {
         this.notify();
      }
   }

   public void authenticationFailed(SaslStreamElements.SASLFailure saslFailure) {
      this.authenticationFailed((Exception)(new SASLErrorException(this.currentMechanism.getName(), saslFailure)));
   }

   public void authenticationFailed(Exception exception) {
      this.saslException = exception;
      synchronized(this) {
         this.notify();
      }
   }

   public boolean authenticationSuccessful() {
      return this.authenticationSuccessful;
   }

   protected void init() {
      this.authenticationSuccessful = false;
      this.saslException = null;
   }

   private SASLMechanism selectMechanism() {
      SASLMechanism selectedMechanism = null;
      Iterator it = REGISTERED_MECHANISMS.iterator();

      while(it.hasNext()) {
         SASLMechanism mechanism = (SASLMechanism)it.next();
         String mechanismName = mechanism.getName();
         synchronized(BLACKLISTED_MECHANISMS) {
            if (BLACKLISTED_MECHANISMS.contains(mechanismName)) {
               continue;
            }
         }

         if (this.serverMechanisms().contains(mechanismName)) {
            selectedMechanism = mechanism.instanceForAuthentication(this.connection);
            break;
         }
      }

      return selectedMechanism;
   }

   private List<String> serverMechanisms() {
      Mechanisms mechanisms = (Mechanisms)this.connection.getFeature("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl");
      if (mechanisms == null) {
         LOGGER.warning("Server did not report any SASL mechanisms");
         return Collections.emptyList();
      } else {
         return mechanisms.getMechanisms();
      }
   }
}
