/*
 * Decompiled with CFR 0.152.
 */
package org.jivesoftware.smack;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.ExceptionCallback;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.SynchronizationPoint;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.filter.IQReplyFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaIdFilter;
import org.jivesoftware.smack.iqrequest.IQRequestHandler;
import org.jivesoftware.smack.packet.Bind;
import org.jivesoftware.smack.packet.ErrorIQ;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Mechanisms;
import org.jivesoftware.smack.packet.PlainStreamElement;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.parsing.UnparsablePacket;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.DNSUtil;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.SmackExecutorThreadFactory;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.dns.HostAddress;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.FullJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.util.XmppStringUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

public abstract class AbstractXMPPConnection
implements XMPPConnection {
    private static final Logger LOGGER = Logger.getLogger(AbstractXMPPConnection.class.getName());
    private static final AtomicInteger connectionCounter = new AtomicInteger(0);
    protected final Set<ConnectionListener> connectionListeners = new CopyOnWriteArraySet<ConnectionListener>();
    private final Collection<PacketCollector> collectors = new ConcurrentLinkedQueue<PacketCollector>();
    private final Map<StanzaListener, ListenerWrapper> syncRecvListeners = new LinkedHashMap<StanzaListener, ListenerWrapper>();
    private final Map<StanzaListener, ListenerWrapper> asyncRecvListeners = new LinkedHashMap<StanzaListener, ListenerWrapper>();
    private final Map<StanzaListener, ListenerWrapper> sendListeners = new HashMap<StanzaListener, ListenerWrapper>();
    private final Map<StanzaListener, InterceptorWrapper> interceptors = new HashMap<StanzaListener, InterceptorWrapper>();
    protected final Lock connectionLock = new ReentrantLock();
    protected final Map<String, ExtensionElement> streamFeatures = new HashMap<String, ExtensionElement>();
    protected FullJid user;
    protected boolean connected = false;
    protected String streamId;
    private long packetReplyTimeout = SmackConfiguration.getDefaultPacketReplyTimeout();
    protected SmackDebugger debugger = null;
    protected Reader reader;
    protected Writer writer;
    protected final SynchronizationPoint<Exception> lastFeaturesReceived = new SynchronizationPoint(this);
    protected final SynchronizationPoint<SmackException> saslFeatureReceived = new SynchronizationPoint(this);
    protected SASLAuthentication saslAuthentication = new SASLAuthentication(this);
    protected final int connectionCounterValue = connectionCounter.getAndIncrement();
    protected final ConnectionConfiguration config;
    private XMPPConnection.FromMode fromMode = XMPPConnection.FromMode.OMITTED;
    protected XMPPInputOutputStream compressionHandler;
    private ParsingExceptionCallback parsingExceptionCallback = SmackConfiguration.getDefaultParsingExceptionCallback();
    private final ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), new SmackExecutorThreadFactory(this.connectionCounterValue, "Incoming Processor"));
    private final ScheduledExecutorService removeCallbacksService = Executors.newSingleThreadScheduledExecutor(new SmackExecutorThreadFactory(this.connectionCounterValue, "Remove Callbacks"));
    private final ExecutorService cachedExecutorService = Executors.newCachedThreadPool(new SmackExecutorThreadFactory(this.connectionCounterValue, "Cached Executor"));
    private final ExecutorService singleThreadedExecutorService = Executors.newSingleThreadExecutor(new SmackExecutorThreadFactory(this.getConnectionCounter(), "Single Threaded Executor"));
    protected String host;
    protected int port;
    protected boolean authenticated = false;
    protected boolean wasAuthenticated = false;
    private final Map<String, IQRequestHandler> setIqRequestHandler = new HashMap<String, IQRequestHandler>();
    private final Map<String, IQRequestHandler> getIqRequestHandler = new HashMap<String, IQRequestHandler>();
    private String usedUsername;
    private String usedPassword;
    private String usedResource;
    private DomainBareJid serviceName;
    protected List<HostAddress> hostAddresses;
    private static boolean replyToUnknownIqDefault;
    private boolean replyToUnkownIq = replyToUnknownIqDefault;
    private long lastStanzaReceived;

    protected static Collection<ConnectionCreationListener> getConnectionCreationListeners() {
        return XMPPConnectionRegistry.getConnectionCreationListeners();
    }

    protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
        this.config = configuration;
    }

    protected ConnectionConfiguration getConfiguration() {
        return this.config;
    }

    @Override
    public DomainBareJid getServiceName() {
        if (this.serviceName != null) {
            return this.serviceName;
        }
        return this.config.getServiceName();
    }

    @Override
    public String getHost() {
        return this.host;
    }

    @Override
    public int getPort() {
        return this.port;
    }

    @Override
    public abstract boolean isSecureConnection();

    protected abstract void sendStanzaInternal(Stanza var1) throws SmackException.NotConnectedException, InterruptedException;

    @Override
    public abstract void send(PlainStreamElement var1) throws SmackException.NotConnectedException, InterruptedException;

    @Override
    public abstract boolean isUsingCompression();

    public synchronized AbstractXMPPConnection connect() throws SmackException, IOException, XMPPException, InterruptedException {
        this.throwAlreadyConnectedExceptionIfAppropriate();
        this.saslAuthentication.init();
        this.saslFeatureReceived.init();
        this.lastFeaturesReceived.init();
        this.streamId = null;
        this.connectInternal();
        return this;
    }

    protected abstract void connectInternal() throws SmackException, IOException, XMPPException, InterruptedException;

    public synchronized void login() throws XMPPException, SmackException, IOException, InterruptedException {
        if (this.isAnonymous()) {
            this.throwNotConnectedExceptionIfAppropriate();
            this.throwAlreadyLoggedInExceptionIfAppropriate();
            this.loginAnonymously();
        } else {
            CharSequence username = this.usedUsername != null ? this.usedUsername : this.config.getUsername();
            String password = this.usedPassword != null ? this.usedPassword : this.config.getPassword();
            String resource = this.usedResource != null ? this.usedResource : this.config.getResource();
            this.login(username, password, resource);
        }
    }

    public synchronized void login(CharSequence username, String password) throws XMPPException, SmackException, IOException, InterruptedException {
        this.login(username, password, this.config.getResource());
    }

    public synchronized void login(CharSequence username, String password, String resource) throws XMPPException, SmackException, IOException, InterruptedException {
        if (!this.config.allowNullOrEmptyUsername) {
            StringUtils.requireNotNullOrEmpty(username, "Username must not be null or empty");
        }
        this.throwNotConnectedExceptionIfAppropriate();
        this.throwAlreadyLoggedInExceptionIfAppropriate();
        this.usedUsername = username != null ? username.toString() : null;
        this.usedPassword = password;
        this.usedResource = resource;
        this.loginNonAnonymously(this.usedUsername, this.usedPassword, this.usedResource);
    }

    protected abstract void loginNonAnonymously(String var1, String var2, String var3) throws XMPPException, SmackException, IOException, InterruptedException;

    protected abstract void loginAnonymously() throws XMPPException, SmackException, IOException, InterruptedException;

    @Override
    public final boolean isConnected() {
        return this.connected;
    }

    @Override
    public final boolean isAuthenticated() {
        return this.authenticated;
    }

    @Override
    public final FullJid getUser() {
        return this.user;
    }

    @Override
    public String getStreamId() {
        if (!this.isConnected()) {
            return null;
        }
        return this.streamId;
    }

    protected void bindResourceAndEstablishSession(String resource) throws XMPPException.XMPPErrorException, IOException, SmackException, InterruptedException {
        LOGGER.finer("Waiting for last features to be received before continuing with resource binding");
        this.lastFeaturesReceived.checkIfSuccessOrWait();
        if (!this.hasFeature("bind", "urn:ietf:params:xml:ns:xmpp-bind")) {
            throw new SmackException.ResourceBindingNotOfferedException();
        }
        Bind bindResource = Bind.newSet(resource);
        PacketCollector packetCollector = this.createPacketCollectorAndSend(new StanzaIdFilter(bindResource), bindResource);
        Bind response = (Bind)packetCollector.nextResultOrThrow();
        this.user = response.getJid();
        this.serviceName = this.user.asDomainBareJid();
        Session.Feature sessionFeature = (Session.Feature)this.getFeature("session", "urn:ietf:params:xml:ns:xmpp-session");
        if (sessionFeature != null && !sessionFeature.isOptional() && !this.getConfiguration().isLegacySessionDisabled()) {
            Session session = new Session();
            packetCollector = this.createPacketCollectorAndSend(new StanzaIdFilter(session), session);
            packetCollector.nextResultOrThrow();
        }
    }

    protected void afterSuccessfulLogin(boolean resumed) throws SmackException.NotConnectedException, InterruptedException {
        this.authenticated = true;
        if (this.config.isDebuggerEnabled() && this.debugger != null) {
            this.debugger.userHasLogged(this.user);
        }
        this.callConnectionAuthenticatedListener(resumed);
        if (this.config.isSendPresence() && !resumed) {
            this.sendStanza(new Presence(Presence.Type.available));
        }
    }

    @Override
    public final boolean isAnonymous() {
        return this.config.getUsername() == null && this.usedUsername == null && !this.config.allowNullOrEmptyUsername;
    }

    protected List<HostAddress> populateHostAddresses() {
        LinkedList<HostAddress> failedAddresses = new LinkedList<HostAddress>();
        if (this.config.host != null) {
            this.hostAddresses = new ArrayList<HostAddress>(1);
            HostAddress hostAddress = new HostAddress(this.config.host, this.config.port);
            this.hostAddresses.add(hostAddress);
        } else {
            this.hostAddresses = DNSUtil.resolveXMPPDomain(this.config.serviceName.toString(), failedAddresses);
        }
        assert (!this.hostAddresses.isEmpty());
        return failedAddresses;
    }

    protected Lock getConnectionLock() {
        return this.connectionLock;
    }

    protected void throwNotConnectedExceptionIfAppropriate() throws SmackException.NotConnectedException {
        if (!this.isConnected()) {
            throw new SmackException.NotConnectedException();
        }
    }

    protected void throwAlreadyConnectedExceptionIfAppropriate() throws SmackException.AlreadyConnectedException {
        if (this.isConnected()) {
            throw new SmackException.AlreadyConnectedException();
        }
    }

    protected void throwAlreadyLoggedInExceptionIfAppropriate() throws SmackException.AlreadyLoggedInException {
        if (this.isAuthenticated()) {
            throw new SmackException.AlreadyLoggedInException();
        }
    }

    @Override
    @Deprecated
    public void sendPacket(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
        this.sendStanza(packet);
    }

    @Override
    public void sendStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
        Objects.requireNonNull(packet, "Packet must not be null");
        this.throwNotConnectedExceptionIfAppropriate();
        switch (this.fromMode) {
            case OMITTED: {
                packet.setFrom((Jid)null);
                break;
            }
            case USER: {
                packet.setFrom(this.getUser());
                break;
            }
        }
        this.firePacketInterceptors(packet);
        this.sendStanzaInternal(packet);
    }

    protected SASLAuthentication getSASLAuthentication() {
        return this.saslAuthentication;
    }

    public void disconnect() {
        try {
            this.disconnect(new Presence(Presence.Type.unavailable));
        }
        catch (SmackException.NotConnectedException e) {
            LOGGER.log(Level.FINEST, "Connection is already disconnected", e);
        }
    }

    public synchronized void disconnect(Presence unavailablePresence) throws SmackException.NotConnectedException {
        try {
            this.sendStanza(unavailablePresence);
        }
        catch (InterruptedException e) {
            LOGGER.log(Level.FINE, "Was interrupted while sending unavailable presence. Continuing to disconnect the connection", e);
        }
        this.shutdown();
        this.callConnectionClosedListener();
    }

    protected abstract void shutdown();

    @Override
    public void addConnectionListener(ConnectionListener connectionListener) {
        if (connectionListener == null) {
            return;
        }
        this.connectionListeners.add(connectionListener);
    }

    @Override
    public void removeConnectionListener(ConnectionListener connectionListener) {
        this.connectionListeners.remove(connectionListener);
    }

    @Override
    public PacketCollector createPacketCollectorAndSend(IQ packet) throws SmackException.NotConnectedException, InterruptedException {
        IQReplyFilter packetFilter = new IQReplyFilter(packet, this);
        PacketCollector packetCollector = this.createPacketCollectorAndSend(packetFilter, packet);
        return packetCollector;
    }

    @Override
    public PacketCollector createPacketCollectorAndSend(StanzaFilter packetFilter, Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
        PacketCollector packetCollector = this.createPacketCollector(packetFilter);
        try {
            this.sendStanza(packet);
        }
        catch (InterruptedException | RuntimeException | SmackException.NotConnectedException e) {
            packetCollector.cancel();
            throw e;
        }
        return packetCollector;
    }

    @Override
    public PacketCollector createPacketCollector(StanzaFilter packetFilter) {
        PacketCollector.Configuration configuration = PacketCollector.newConfiguration().setStanzaFilter(packetFilter);
        return this.createPacketCollector(configuration);
    }

    @Override
    public PacketCollector createPacketCollector(PacketCollector.Configuration configuration) {
        PacketCollector collector = new PacketCollector(this, configuration);
        this.collectors.add(collector);
        return collector;
    }

    @Override
    public void removePacketCollector(PacketCollector collector) {
        this.collectors.remove(collector);
    }

    @Override
    @Deprecated
    public void addPacketListener(StanzaListener packetListener, StanzaFilter packetFilter) {
        this.addAsyncStanzaListener(packetListener, packetFilter);
    }

    @Override
    @Deprecated
    public boolean removePacketListener(StanzaListener packetListener) {
        return this.removeAsyncStanzaListener(packetListener);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public void addSyncStanzaListener(StanzaListener packetListener, StanzaFilter packetFilter) {
        if (packetListener == null) {
            throw new NullPointerException("Packet listener is null.");
        }
        ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
        Map<StanzaListener, ListenerWrapper> map = this.syncRecvListeners;
        synchronized (map) {
            this.syncRecvListeners.put(packetListener, wrapper);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public boolean removeSyncStanzaListener(StanzaListener packetListener) {
        Map<StanzaListener, ListenerWrapper> map = this.syncRecvListeners;
        synchronized (map) {
            return this.syncRecvListeners.remove(packetListener) != null;
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public void addAsyncStanzaListener(StanzaListener packetListener, StanzaFilter packetFilter) {
        if (packetListener == null) {
            throw new NullPointerException("Packet listener is null.");
        }
        ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
        Map<StanzaListener, ListenerWrapper> map = this.asyncRecvListeners;
        synchronized (map) {
            this.asyncRecvListeners.put(packetListener, wrapper);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public boolean removeAsyncStanzaListener(StanzaListener packetListener) {
        Map<StanzaListener, ListenerWrapper> map = this.asyncRecvListeners;
        synchronized (map) {
            return this.asyncRecvListeners.remove(packetListener) != null;
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public void addPacketSendingListener(StanzaListener packetListener, StanzaFilter packetFilter) {
        if (packetListener == null) {
            throw new NullPointerException("Packet listener is null.");
        }
        ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
        Map<StanzaListener, ListenerWrapper> map = this.sendListeners;
        synchronized (map) {
            this.sendListeners.put(packetListener, wrapper);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public void removePacketSendingListener(StanzaListener packetListener) {
        Map<StanzaListener, ListenerWrapper> map = this.sendListeners;
        synchronized (map) {
            this.sendListeners.remove(packetListener);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void firePacketSendingListeners(final Stanza packet) {
        final LinkedList<StanzaListener> listenersToNotify = new LinkedList<StanzaListener>();
        Map<StanzaListener, ListenerWrapper> map = this.sendListeners;
        synchronized (map) {
            for (ListenerWrapper listenerWrapper : this.sendListeners.values()) {
                if (!listenerWrapper.filterMatches(packet)) continue;
                listenersToNotify.add(listenerWrapper.getListener());
            }
        }
        if (listenersToNotify.isEmpty()) {
            return;
        }
        this.asyncGo(new Runnable(){

            @Override
            public void run() {
                for (StanzaListener listener : listenersToNotify) {
                    try {
                        listener.processPacket(packet);
                    }
                    catch (Exception e) {
                        LOGGER.log(Level.WARNING, "Sending listener threw exception", e);
                    }
                }
            }
        });
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public void addPacketInterceptor(StanzaListener packetInterceptor, StanzaFilter packetFilter) {
        if (packetInterceptor == null) {
            throw new NullPointerException("Packet interceptor is null.");
        }
        InterceptorWrapper interceptorWrapper = new InterceptorWrapper(packetInterceptor, packetFilter);
        Map<StanzaListener, InterceptorWrapper> map = this.interceptors;
        synchronized (map) {
            this.interceptors.put(packetInterceptor, interceptorWrapper);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public void removePacketInterceptor(StanzaListener packetInterceptor) {
        Map<StanzaListener, InterceptorWrapper> map = this.interceptors;
        synchronized (map) {
            this.interceptors.remove(packetInterceptor);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void firePacketInterceptors(Stanza packet) {
        LinkedList<StanzaListener> interceptorsToInvoke = new LinkedList<StanzaListener>();
        Map<StanzaListener, InterceptorWrapper> map = this.interceptors;
        synchronized (map) {
            for (InterceptorWrapper interceptorWrapper : this.interceptors.values()) {
                if (!interceptorWrapper.filterMatches(packet)) continue;
                interceptorsToInvoke.add(interceptorWrapper.getInterceptor());
            }
        }
        for (StanzaListener interceptor : interceptorsToInvoke) {
            try {
                interceptor.processPacket(packet);
            }
            catch (Exception e) {
                LOGGER.log(Level.SEVERE, "Packet interceptor threw exception", e);
            }
        }
    }

    protected void initDebugger() {
        if (this.reader == null || this.writer == null) {
            throw new NullPointerException("Reader or writer isn't initialized.");
        }
        if (this.config.isDebuggerEnabled()) {
            if (this.debugger == null) {
                this.debugger = SmackConfiguration.createDebugger(this, this.writer, this.reader);
            }
            if (this.debugger == null) {
                LOGGER.severe("Debugging enabled but could not find debugger class");
            } else {
                this.reader = this.debugger.newConnectionReader(this.reader);
                this.writer = this.debugger.newConnectionWriter(this.writer);
            }
        }
    }

    @Override
    public long getPacketReplyTimeout() {
        return this.packetReplyTimeout;
    }

    @Override
    public void setPacketReplyTimeout(long timeout) {
        this.packetReplyTimeout = timeout;
    }

    public static void setReplyToUnknownIqDefault(boolean replyToUnkownIqDefault) {
        replyToUnknownIqDefault = replyToUnkownIqDefault;
    }

    public void setReplyToUnknownIq(boolean replyToUnknownIq) {
        this.replyToUnkownIq = replyToUnknownIq;
    }

    protected void parseAndProcessStanza(XmlPullParser parser) throws Exception {
        Stanza stanza;
        block3: {
            ParserUtils.assertAtStartTag(parser);
            int parserDepth = parser.getDepth();
            stanza = null;
            try {
                stanza = PacketParserUtils.parseStanza(parser);
            }
            catch (Exception e) {
                CharSequence content = PacketParserUtils.parseContentDepth(parser, parserDepth);
                UnparsablePacket message = new UnparsablePacket(content, e);
                ParsingExceptionCallback callback = this.getParsingExceptionCallback();
                if (callback == null) break block3;
                callback.handleUnparsablePacket(message);
            }
        }
        ParserUtils.assertAtEndTag(parser);
        if (stanza != null) {
            this.processPacket(stanza);
        }
    }

    protected void processPacket(Stanza packet) {
        assert (packet != null);
        this.lastStanzaReceived = System.currentTimeMillis();
        this.executorService.submit(new ListenerNotification(packet));
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void invokePacketCollectorsAndNotifyRecvListeners(final Stanza packet) {
        Object type;
        if (packet instanceof IQ) {
            final IQ iq = (IQ)packet;
            type = iq.getType();
            switch (9.$SwitchMap$org$jivesoftware$smack$packet$IQ$Type[((Enum)type).ordinal()]) {
                case 1: 
                case 2: {
                    String key = XmppStringUtils.generateKey(iq.getChildElementName(), iq.getChildElementNamespace());
                    IQRequestHandler iqRequestHandler = null;
                    switch (9.$SwitchMap$org$jivesoftware$smack$packet$IQ$Type[((Enum)type).ordinal()]) {
                        case 1: {
                            Map<String, IQRequestHandler> map = this.setIqRequestHandler;
                            synchronized (map) {
                                iqRequestHandler = this.setIqRequestHandler.get(key);
                                break;
                            }
                        }
                        case 2: {
                            Map<String, IQRequestHandler> map = this.getIqRequestHandler;
                            synchronized (map) {
                                iqRequestHandler = this.getIqRequestHandler.get(key);
                                break;
                            }
                        }
                        default: {
                            throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'");
                        }
                    }
                    if (iqRequestHandler == null) {
                        if (!this.replyToUnkownIq) {
                            return;
                        }
                        ErrorIQ errorIQ = IQ.createErrorResponse(iq, new XMPPError(XMPPError.Condition.feature_not_implemented));
                        try {
                            this.sendStanza(errorIQ);
                        }
                        catch (InterruptedException | SmackException.NotConnectedException e) {
                            LOGGER.log(Level.WARNING, "Exception while sending error IQ to unkown IQ request", e);
                        }
                        break;
                    }
                    ExecutorService executorService = null;
                    switch (iqRequestHandler.getMode()) {
                        case sync: {
                            executorService = this.singleThreadedExecutorService;
                            break;
                        }
                        case async: {
                            executorService = this.cachedExecutorService;
                        }
                    }
                    final IQRequestHandler finalIqRequestHandler = iqRequestHandler;
                    executorService.execute(new Runnable(){

                        @Override
                        public void run() {
                            IQ response = finalIqRequestHandler.handleIQRequest(iq);
                            if (response == null) {
                                return;
                            }
                            try {
                                AbstractXMPPConnection.this.sendStanza(response);
                            }
                            catch (InterruptedException | SmackException.NotConnectedException e) {
                                LOGGER.log(Level.WARNING, "Exception while sending response to IQ request", e);
                            }
                        }
                    });
                    return;
                }
            }
        }
        final LinkedList<StanzaListener> listenersToNotify = new LinkedList<StanzaListener>();
        type = this.asyncRecvListeners;
        synchronized (type) {
            for (ListenerWrapper listenerWrapper : this.asyncRecvListeners.values()) {
                if (!listenerWrapper.filterMatches(packet)) continue;
                listenersToNotify.add(listenerWrapper.getListener());
            }
        }
        for (final StanzaListener listener : listenersToNotify) {
            this.asyncGo(new Runnable(){

                @Override
                public void run() {
                    try {
                        listener.processPacket(packet);
                    }
                    catch (Exception e) {
                        LOGGER.log(Level.SEVERE, "Exception in async packet listener", e);
                    }
                }
            });
        }
        for (PacketCollector collector : this.collectors) {
            collector.processPacket(packet);
        }
        listenersToNotify.clear();
        Map<StanzaListener, ListenerWrapper> map = this.syncRecvListeners;
        synchronized (map) {
            for (ListenerWrapper listenerWrapper : this.syncRecvListeners.values()) {
                if (!listenerWrapper.filterMatches(packet)) continue;
                listenersToNotify.add(listenerWrapper.getListener());
            }
        }
        this.singleThreadedExecutorService.execute(new Runnable(){

            @Override
            public void run() {
                for (StanzaListener listener : listenersToNotify) {
                    try {
                        listener.processPacket(packet);
                    }
                    catch (SmackException.NotConnectedException e) {
                        LOGGER.log(Level.WARNING, "Got not connected exception, aborting", e);
                        break;
                    }
                    catch (Exception e) {
                        LOGGER.log(Level.SEVERE, "Exception in packet listener", e);
                    }
                }
            }
        });
    }

    protected void setWasAuthenticated() {
        if (!this.wasAuthenticated) {
            this.wasAuthenticated = this.authenticated;
        }
    }

    protected void callConnectionConnectedListener() {
        for (ConnectionListener listener : this.connectionListeners) {
            listener.connected(this);
        }
    }

    protected void callConnectionAuthenticatedListener(boolean resumed) {
        for (ConnectionListener listener : this.connectionListeners) {
            try {
                listener.authenticated(this, resumed);
            }
            catch (Exception e) {
                LOGGER.log(Level.SEVERE, "Exception in authenticated listener", e);
            }
        }
    }

    void callConnectionClosedListener() {
        for (ConnectionListener listener : this.connectionListeners) {
            try {
                listener.connectionClosed();
            }
            catch (Exception e) {
                LOGGER.log(Level.SEVERE, "Error in listener while closing connection", e);
            }
        }
    }

    protected void callConnectionClosedOnErrorListener(Exception e) {
        LOGGER.log(Level.WARNING, "Connection closed with error", e);
        for (ConnectionListener listener : this.connectionListeners) {
            try {
                listener.connectionClosedOnError(e);
            }
            catch (Exception e2) {
                LOGGER.log(Level.SEVERE, "Error in listener while closing connection", e2);
            }
        }
    }

    protected void notifyReconnection() {
        for (ConnectionListener listener : this.connectionListeners) {
            try {
                listener.reconnectionSuccessful();
            }
            catch (Exception e) {
                LOGGER.log(Level.WARNING, "notifyReconnection()", e);
            }
        }
    }

    @Override
    public int getConnectionCounter() {
        return this.connectionCounterValue;
    }

    @Override
    public void setFromMode(XMPPConnection.FromMode fromMode) {
        this.fromMode = fromMode;
    }

    @Override
    public XMPPConnection.FromMode getFromMode() {
        return this.fromMode;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void finalize() throws Throwable {
        LOGGER.fine("finalizing XMPPConnection ( " + this.getConnectionCounter() + "): Shutting down executor services");
        try {
            this.executorService.shutdownNow();
            this.cachedExecutorService.shutdown();
            this.removeCallbacksService.shutdownNow();
            this.singleThreadedExecutorService.shutdownNow();
        }
        catch (Throwable t) {
            LOGGER.log(Level.WARNING, "finalize() threw trhowable", t);
        }
        finally {
            super.finalize();
        }
    }

    protected final void parseFeatures(XmlPullParser parser) throws XmlPullParserException, IOException, SmackException, InterruptedException {
        this.streamFeatures.clear();
        int initialDepth = parser.getDepth();
        while (true) {
            int eventType;
            if ((eventType = parser.next()) == 2 && parser.getDepth() == initialDepth + 1) {
                ExtensionElement streamFeature = null;
                String name = parser.getName();
                String namespace = parser.getNamespace();
                switch (name) {
                    case "starttls": {
                        streamFeature = PacketParserUtils.parseStartTlsFeature(parser);
                        break;
                    }
                    case "mechanisms": {
                        streamFeature = new Mechanisms(PacketParserUtils.parseMechanisms(parser));
                        break;
                    }
                    case "bind": {
                        streamFeature = Bind.Feature.INSTANCE;
                        break;
                    }
                    case "session": {
                        streamFeature = PacketParserUtils.parseSessionFeature(parser);
                        break;
                    }
                    case "compression": {
                        streamFeature = PacketParserUtils.parseCompressionFeature(parser);
                        break;
                    }
                    default: {
                        ExtensionElementProvider<ExtensionElement> provider = ProviderManager.getStreamFeatureProvider(name, namespace);
                        if (provider == null) break;
                        streamFeature = (ExtensionElement)provider.parse(parser);
                    }
                }
                if (streamFeature == null) continue;
                this.addStreamFeature(streamFeature);
                continue;
            }
            if (eventType == 3 && parser.getDepth() == initialDepth) break;
        }
        if (this.hasFeature("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl") && (!this.hasFeature("starttls", "urn:ietf:params:xml:ns:xmpp-tls") || this.config.getSecurityMode() == ConnectionConfiguration.SecurityMode.disabled)) {
            this.saslFeatureReceived.reportSuccess();
        }
        if (!(!this.hasFeature("bind", "urn:ietf:params:xml:ns:xmpp-bind") || this.hasFeature("compression", "http://jabber.org/protocol/compress") && this.config.isCompressionEnabled())) {
            this.lastFeaturesReceived.reportSuccess();
        }
        this.afterFeaturesReceived();
    }

    protected void afterFeaturesReceived() throws SmackException.SecurityRequiredException, SmackException.NotConnectedException, InterruptedException {
    }

    @Override
    public <F extends ExtensionElement> F getFeature(String element, String namespace) {
        return (F)this.streamFeatures.get(XmppStringUtils.generateKey(element, namespace));
    }

    @Override
    public boolean hasFeature(String element, String namespace) {
        return this.getFeature(element, namespace) != null;
    }

    private void addStreamFeature(ExtensionElement feature) {
        String key = XmppStringUtils.generateKey(feature.getElementName(), feature.getNamespace());
        this.streamFeatures.put(key, feature);
    }

    @Override
    public void sendStanzaWithResponseCallback(Stanza stanza, StanzaFilter replyFilter, StanzaListener callback) throws SmackException.NotConnectedException, InterruptedException {
        this.sendStanzaWithResponseCallback(stanza, replyFilter, callback, null);
    }

    @Override
    public void sendStanzaWithResponseCallback(Stanza stanza, StanzaFilter replyFilter, StanzaListener callback, ExceptionCallback exceptionCallback) throws SmackException.NotConnectedException, InterruptedException {
        this.sendStanzaWithResponseCallback(stanza, replyFilter, callback, exceptionCallback, this.getPacketReplyTimeout());
    }

    @Override
    public void sendStanzaWithResponseCallback(Stanza stanza, final StanzaFilter replyFilter, final StanzaListener callback, final ExceptionCallback exceptionCallback, long timeout) throws SmackException.NotConnectedException, InterruptedException {
        Objects.requireNonNull(stanza, "stanza must not be null");
        Objects.requireNonNull(replyFilter, "replyFilter must not be null");
        Objects.requireNonNull(callback, "callback must not be null");
        final StanzaListener packetListener = new StanzaListener(){

            /*
             * WARNING - Removed try catching itself - possible behaviour change.
             */
            @Override
            public void processPacket(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
                try {
                    XMPPException.XMPPErrorException.ifHasErrorThenThrow(packet);
                    callback.processPacket(packet);
                }
                catch (XMPPException.XMPPErrorException e) {
                    if (exceptionCallback != null) {
                        exceptionCallback.processException(e);
                    }
                }
                finally {
                    AbstractXMPPConnection.this.removeAsyncStanzaListener(this);
                }
            }
        };
        this.removeCallbacksService.schedule(new Runnable(){

            @Override
            public void run() {
                boolean removed = AbstractXMPPConnection.this.removeAsyncStanzaListener(packetListener);
                if (removed && exceptionCallback != null) {
                    exceptionCallback.processException(SmackException.NoResponseException.newWith((XMPPConnection)AbstractXMPPConnection.this, replyFilter));
                }
            }
        }, timeout, TimeUnit.MILLISECONDS);
        this.addAsyncStanzaListener(packetListener, replyFilter);
        this.sendStanza(stanza);
    }

    @Override
    public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback) throws SmackException.NotConnectedException, InterruptedException {
        this.sendIqWithResponseCallback(iqRequest, callback, null);
    }

    @Override
    public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback, ExceptionCallback exceptionCallback) throws SmackException.NotConnectedException, InterruptedException {
        this.sendIqWithResponseCallback(iqRequest, callback, exceptionCallback, this.getPacketReplyTimeout());
    }

    @Override
    public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback, ExceptionCallback exceptionCallback, long timeout) throws SmackException.NotConnectedException, InterruptedException {
        IQReplyFilter replyFilter = new IQReplyFilter(iqRequest, this);
        this.sendStanzaWithResponseCallback(iqRequest, replyFilter, callback, exceptionCallback, timeout);
    }

    @Override
    public void addOneTimeSyncCallback(final StanzaListener callback, StanzaFilter packetFilter) {
        final StanzaListener packetListener = new StanzaListener(){

            /*
             * WARNING - Removed try catching itself - possible behaviour change.
             */
            @Override
            public void processPacket(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
                try {
                    callback.processPacket(packet);
                }
                finally {
                    AbstractXMPPConnection.this.removeSyncStanzaListener(this);
                }
            }
        };
        this.addSyncStanzaListener(packetListener, packetFilter);
        this.removeCallbacksService.schedule(new Runnable(){

            @Override
            public void run() {
                AbstractXMPPConnection.this.removeSyncStanzaListener(packetListener);
            }
        }, this.getPacketReplyTimeout(), TimeUnit.MILLISECONDS);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public IQRequestHandler registerIQRequestHandler(IQRequestHandler iqRequestHandler) {
        String key = XmppStringUtils.generateKey(iqRequestHandler.getElement(), iqRequestHandler.getNamespace());
        switch (iqRequestHandler.getType()) {
            case set: {
                Map<String, IQRequestHandler> map = this.setIqRequestHandler;
                synchronized (map) {
                    return this.setIqRequestHandler.put(key, iqRequestHandler);
                }
            }
            case get: {
                Map<String, IQRequestHandler> map = this.getIqRequestHandler;
                synchronized (map) {
                    return this.getIqRequestHandler.put(key, iqRequestHandler);
                }
            }
        }
        throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed");
    }

    @Override
    public final IQRequestHandler unregisterIQRequestHandler(IQRequestHandler iqRequestHandler) {
        return this.unregisterIQRequestHandler(iqRequestHandler.getElement(), iqRequestHandler.getNamespace(), iqRequestHandler.getType());
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) {
        String key = XmppStringUtils.generateKey(element, namespace);
        switch (type) {
            case set: {
                Map<String, IQRequestHandler> map = this.setIqRequestHandler;
                synchronized (map) {
                    return this.setIqRequestHandler.remove(key);
                }
            }
            case get: {
                Map<String, IQRequestHandler> map = this.getIqRequestHandler;
                synchronized (map) {
                    return this.getIqRequestHandler.remove(key);
                }
            }
        }
        throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed");
    }

    @Override
    public long getLastStanzaReceived() {
        return this.lastStanzaReceived;
    }

    public void setParsingExceptionCallback(ParsingExceptionCallback callback) {
        this.parsingExceptionCallback = callback;
    }

    public ParsingExceptionCallback getParsingExceptionCallback() {
        return this.parsingExceptionCallback;
    }

    protected final void asyncGo(Runnable runnable) {
        this.cachedExecutorService.execute(runnable);
    }

    protected final ScheduledFuture<?> schedule(Runnable runnable, long delay, TimeUnit unit) {
        return this.removeCallbacksService.schedule(runnable, delay, unit);
    }

    static {
        SmackConfiguration.getVersion();
        replyToUnknownIqDefault = true;
    }

    protected static class InterceptorWrapper {
        private final StanzaListener packetInterceptor;
        private final StanzaFilter packetFilter;

        public InterceptorWrapper(StanzaListener packetInterceptor, StanzaFilter packetFilter) {
            this.packetInterceptor = packetInterceptor;
            this.packetFilter = packetFilter;
        }

        public boolean filterMatches(Stanza packet) {
            return this.packetFilter == null || this.packetFilter.accept(packet);
        }

        public StanzaListener getInterceptor() {
            return this.packetInterceptor;
        }
    }

    protected static class ListenerWrapper {
        private final StanzaListener packetListener;
        private final StanzaFilter packetFilter;

        public ListenerWrapper(StanzaListener packetListener, StanzaFilter packetFilter) {
            this.packetListener = packetListener;
            this.packetFilter = packetFilter;
        }

        public boolean filterMatches(Stanza packet) {
            return this.packetFilter == null || this.packetFilter.accept(packet);
        }

        public StanzaListener getListener() {
            return this.packetListener;
        }
    }

    private class ListenerNotification
    implements Runnable {
        private final Stanza packet;

        public ListenerNotification(Stanza packet) {
            this.packet = packet;
        }

        @Override
        public void run() {
            AbstractXMPPConnection.this.invokePacketCollectorsAndNotifyRecvListeners(this.packet);
        }
    }
}
