/*
 * Decompiled with CFR 0.152.
 */
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.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
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;

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

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static void registerSASLMechanism(SASLMechanism mechanism) {
        List<SASLMechanism> list = REGISTERED_MECHANISMS;
        synchronized (list) {
            REGISTERED_MECHANISMS.add(mechanism);
            Collections.sort(REGISTERED_MECHANISMS);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static Map<String, String> getRegisterdSASLMechanisms() {
        HashMap<String, String> answer = new HashMap<String, String>();
        List<SASLMechanism> list = REGISTERED_MECHANISMS;
        synchronized (list) {
            for (SASLMechanism mechanism : REGISTERED_MECHANISMS) {
                answer.put(mechanism.getClass().getName(), mechanism.getName());
            }
        }
        return answer;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static boolean unregisterSASLMechanism(String clazz) {
        List<SASLMechanism> list = REGISTERED_MECHANISMS;
        synchronized (list) {
            Iterator<SASLMechanism> it = REGISTERED_MECHANISMS.iterator();
            while (it.hasNext()) {
                SASLMechanism mechanism = it.next();
                if (!mechanism.getClass().getName().equals(clazz)) continue;
                it.remove();
                return true;
            }
        }
        return false;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static boolean blacklistSASLMechanism(String mechansim) {
        Set<String> set = BLACKLISTED_MECHANISMS;
        synchronized (set) {
            return BLACKLISTED_MECHANISMS.add(mechansim);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static boolean unBlacklistSASLMechanism(String mechanism) {
        Set<String> set = BLACKLISTED_MECHANISMS;
        synchronized (set) {
            return BLACKLISTED_MECHANISMS.remove(mechanism);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static Set<String> getBlacklistedSASLMechanisms() {
        Set<String> set = BLACKLISTED_MECHANISMS;
        synchronized (set) {
            return new HashSet<String>(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());
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void authenticate(String resource, CallbackHandler cbh) throws IOException, XMPPException.XMPPErrorException, SASLErrorException, SmackException, InterruptedException {
        SASLMechanism selectedMechanism = this.selectMechanism();
        if (selectedMechanism != null) {
            this.currentMechanism = selectedMechanism;
            SASLAuthentication sASLAuthentication = this;
            synchronized (sASLAuthentication) {
                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.");
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    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;
            SASLAuthentication sASLAuthentication = this;
            synchronized (sASLAuthentication) {
                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.");
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void authenticateAnonymously() throws SASLErrorException, SmackException, XMPPException.XMPPErrorException, InterruptedException {
        this.currentMechanism = new SASLAnonymous().instanceForAuthentication(this.connection);
        SASLAuthentication sASLAuthentication = this;
        synchronized (sASLAuthentication) {
            this.currentMechanism.authenticate(null, null, 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;
            }
            if (this.saslException instanceof SASLErrorException) {
                throw (SASLErrorException)this.saslException;
            }
            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 (InterruptedException | SmackException e) {
            this.authenticationFailed(e);
            throw e;
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void authenticated(SaslStreamElements.Success success) throws SmackException, InterruptedException {
        if (success.getData() != null) {
            this.challengeReceived(success.getData(), true);
        }
        this.currentMechanism.checkIfSuccessfulOrThrow();
        this.authenticationSuccessful = true;
        SASLAuthentication sASLAuthentication = this;
        synchronized (sASLAuthentication) {
            this.notify();
        }
    }

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

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void authenticationFailed(Exception exception) {
        this.saslException = exception;
        SASLAuthentication sASLAuthentication = this;
        synchronized (sASLAuthentication) {
            this.notify();
        }
    }

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

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

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private SASLMechanism selectMechanism() {
        SASLMechanism selectedMechanism = null;
        for (SASLMechanism mechanism : REGISTERED_MECHANISMS) {
            String mechanismName = mechanism.getName();
            Set<String> set = BLACKLISTED_MECHANISMS;
            synchronized (set) {
                if (BLACKLISTED_MECHANISMS.contains(mechanismName)) {
                    continue;
                }
            }
            if (!this.serverMechanisms().contains(mechanismName)) continue;
            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();
        }
        return mechanisms.getMechanisms();
    }
}
