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.Iterator;
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.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();
   private final Collection<PacketCollector> collectors = new ConcurrentLinkedQueue();
   private final Map<StanzaListener, AbstractXMPPConnection.ListenerWrapper> syncRecvListeners = new LinkedHashMap();
   private final Map<StanzaListener, AbstractXMPPConnection.ListenerWrapper> asyncRecvListeners = new LinkedHashMap();
   private final Map<StanzaListener, AbstractXMPPConnection.ListenerWrapper> sendListeners = new HashMap();
   private final Map<StanzaListener, AbstractXMPPConnection.InterceptorWrapper> interceptors = new HashMap();
   protected final Lock connectionLock = new ReentrantLock();
   protected final Map<String, ExtensionElement> streamFeatures = new HashMap();
   protected FullJid user;
   protected boolean connected = false;
   protected String streamId;
   private long packetReplyTimeout = (long)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;
   protected final ConnectionConfiguration config;
   private XMPPConnection.FromMode fromMode;
   protected XMPPInputOutputStream compressionHandler;
   private ParsingExceptionCallback parsingExceptionCallback;
   private final ThreadPoolExecutor executorService;
   private final ScheduledExecutorService removeCallbacksService;
   private final ExecutorService cachedExecutorService;
   private final ExecutorService singleThreadedExecutorService;
   protected String host;
   protected int port;
   protected boolean authenticated;
   protected boolean wasAuthenticated;
   private final Map<String, IQRequestHandler> setIqRequestHandler;
   private final Map<String, IQRequestHandler> getIqRequestHandler;
   private String usedUsername;
   private String usedPassword;
   private String usedResource;
   private DomainBareJid serviceName;
   protected List<HostAddress> hostAddresses;
   private static boolean replyToUnknownIqDefault;
   private boolean replyToUnkownIq;
   private long lastStanzaReceived;

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

   protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
      this.connectionCounterValue = connectionCounter.getAndIncrement();
      this.fromMode = XMPPConnection.FromMode.OMITTED;
      this.parsingExceptionCallback = SmackConfiguration.getDefaultParsingExceptionCallback();
      this.executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue(100), new SmackExecutorThreadFactory(this.connectionCounterValue, "Incoming Processor"));
      this.removeCallbacksService = Executors.newSingleThreadScheduledExecutor(new SmackExecutorThreadFactory(this.connectionCounterValue, "Remove Callbacks"));
      this.cachedExecutorService = Executors.newCachedThreadPool(new SmackExecutorThreadFactory(this.connectionCounterValue, "Cached Executor"));
      this.singleThreadedExecutorService = Executors.newSingleThreadExecutor(new SmackExecutorThreadFactory(this.getConnectionCounter(), "Single Threaded Executor"));
      this.authenticated = false;
      this.wasAuthenticated = false;
      this.setIqRequestHandler = new HashMap();
      this.getIqRequestHandler = new HashMap();
      this.replyToUnkownIq = replyToUnknownIqDefault;
      this.config = configuration;
   }

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

   public DomainBareJid getServiceName() {
      return this.serviceName != null ? this.serviceName : this.config.getServiceName();
   }

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

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

   public abstract boolean isSecureConnection();

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

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

   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((CharSequence)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;

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

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

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

   public String getStreamId() {
      return !this.isConnected() ? null : 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();
      } else {
         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));
      }

   }

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

   protected List<HostAddress> populateHostAddresses() {
      List<HostAddress> failedAddresses = new LinkedList();
      if (this.config.host != null) {
         this.hostAddresses = new ArrayList(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();
      }
   }

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

   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((Jid)this.getUser());
      case UNCHANGED:
      }

      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 var2) {
         LOGGER.log(Level.FINEST, "Connection is already disconnected", var2);
      }

   }

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

      this.shutdown();
      this.callConnectionClosedListener();
   }

   protected abstract void shutdown();

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

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

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

   public PacketCollector createPacketCollectorAndSend(StanzaFilter packetFilter, Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
      PacketCollector packetCollector = this.createPacketCollector(packetFilter);

      try {
         this.sendStanza(packet);
         return packetCollector;
      } catch (SmackException.NotConnectedException | RuntimeException | InterruptedException var5) {
         packetCollector.cancel();
         throw var5;
      }
   }

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

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

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

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

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

   public void addSyncStanzaListener(StanzaListener packetListener, StanzaFilter packetFilter) {
      if (packetListener == null) {
         throw new NullPointerException("Packet listener is null.");
      } else {
         AbstractXMPPConnection.ListenerWrapper wrapper = new AbstractXMPPConnection.ListenerWrapper(packetListener, packetFilter);
         synchronized(this.syncRecvListeners) {
            this.syncRecvListeners.put(packetListener, wrapper);
         }
      }
   }

   public boolean removeSyncStanzaListener(StanzaListener packetListener) {
      synchronized(this.syncRecvListeners) {
         return this.syncRecvListeners.remove(packetListener) != null;
      }
   }

   public void addAsyncStanzaListener(StanzaListener packetListener, StanzaFilter packetFilter) {
      if (packetListener == null) {
         throw new NullPointerException("Packet listener is null.");
      } else {
         AbstractXMPPConnection.ListenerWrapper wrapper = new AbstractXMPPConnection.ListenerWrapper(packetListener, packetFilter);
         synchronized(this.asyncRecvListeners) {
            this.asyncRecvListeners.put(packetListener, wrapper);
         }
      }
   }

   public boolean removeAsyncStanzaListener(StanzaListener packetListener) {
      synchronized(this.asyncRecvListeners) {
         return this.asyncRecvListeners.remove(packetListener) != null;
      }
   }

   public void addPacketSendingListener(StanzaListener packetListener, StanzaFilter packetFilter) {
      if (packetListener == null) {
         throw new NullPointerException("Packet listener is null.");
      } else {
         AbstractXMPPConnection.ListenerWrapper wrapper = new AbstractXMPPConnection.ListenerWrapper(packetListener, packetFilter);
         synchronized(this.sendListeners) {
            this.sendListeners.put(packetListener, wrapper);
         }
      }
   }

   public void removePacketSendingListener(StanzaListener packetListener) {
      synchronized(this.sendListeners) {
         this.sendListeners.remove(packetListener);
      }
   }

   protected void firePacketSendingListeners(final Stanza packet) {
      final List<StanzaListener> listenersToNotify = new LinkedList();
      synchronized(this.sendListeners) {
         Iterator i$ = this.sendListeners.values().iterator();

         while(true) {
            if (!i$.hasNext()) {
               break;
            }

            AbstractXMPPConnection.ListenerWrapper listenerWrapper = (AbstractXMPPConnection.ListenerWrapper)i$.next();
            if (listenerWrapper.filterMatches(packet)) {
               listenersToNotify.add(listenerWrapper.getListener());
            }
         }
      }

      if (!listenersToNotify.isEmpty()) {
         this.asyncGo(new Runnable() {
            public void run() {
               Iterator i$ = listenersToNotify.iterator();

               while(i$.hasNext()) {
                  StanzaListener listener = (StanzaListener)i$.next();

                  try {
                     listener.processPacket(packet);
                  } catch (Exception var4) {
                     AbstractXMPPConnection.LOGGER.log(Level.WARNING, "Sending listener threw exception", var4);
                  }
               }

            }
         });
      }
   }

   public void addPacketInterceptor(StanzaListener packetInterceptor, StanzaFilter packetFilter) {
      if (packetInterceptor == null) {
         throw new NullPointerException("Packet interceptor is null.");
      } else {
         AbstractXMPPConnection.InterceptorWrapper interceptorWrapper = new AbstractXMPPConnection.InterceptorWrapper(packetInterceptor, packetFilter);
         synchronized(this.interceptors) {
            this.interceptors.put(packetInterceptor, interceptorWrapper);
         }
      }
   }

   public void removePacketInterceptor(StanzaListener packetInterceptor) {
      synchronized(this.interceptors) {
         this.interceptors.remove(packetInterceptor);
      }
   }

   private void firePacketInterceptors(Stanza packet) {
      List<StanzaListener> interceptorsToInvoke = new LinkedList();
      synchronized(this.interceptors) {
         Iterator i$ = this.interceptors.values().iterator();

         while(i$.hasNext()) {
            AbstractXMPPConnection.InterceptorWrapper interceptorWrapper = (AbstractXMPPConnection.InterceptorWrapper)i$.next();
            if (interceptorWrapper.filterMatches(packet)) {
               interceptorsToInvoke.add(interceptorWrapper.getInterceptor());
            }
         }
      }

      Iterator i$ = interceptorsToInvoke.iterator();

      while(i$.hasNext()) {
         StanzaListener interceptor = (StanzaListener)i$.next();

         try {
            interceptor.processPacket(packet);
         } catch (Exception var7) {
            LOGGER.log(Level.SEVERE, "Packet interceptor threw exception", var7);
         }
      }

   }

   protected void initDebugger() {
      if (this.reader != null && this.writer != null) {
         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);
            }
         }

      } else {
         throw new NullPointerException("Reader or writer isn't initialized.");
      }
   }

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

   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 {
      ParserUtils.assertAtStartTag(parser);
      int parserDepth = parser.getDepth();
      Stanza stanza = null;

      try {
         stanza = PacketParserUtils.parseStanza(parser);
      } catch (Exception var8) {
         CharSequence content = PacketParserUtils.parseContentDepth(parser, parserDepth);
         UnparsablePacket message = new UnparsablePacket(content, var8);
         ParsingExceptionCallback callback = this.getParsingExceptionCallback();
         if (callback != null) {
            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 AbstractXMPPConnection.ListenerNotification(packet));
   }

   protected void invokePacketCollectorsAndNotifyRecvListeners(final Stanza packet) {
      if (packet instanceof IQ) {
         final IQ iq = (IQ)packet;
         IQ.Type type = iq.getType();
         switch(type) {
         case set:
         case get:
            String key = XmppStringUtils.generateKey(iq.getChildElementName(), iq.getChildElementNamespace());
            final IQRequestHandler iqRequestHandler = null;
            switch(type) {
            case set:
               synchronized(this.setIqRequestHandler) {
                  iqRequestHandler = (IQRequestHandler)this.setIqRequestHandler.get(key);
                  break;
               }
            case get:
               synchronized(this.getIqRequestHandler) {
                  iqRequestHandler = (IQRequestHandler)this.getIqRequestHandler.get(key);
                  break;
               }
            default:
               throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'");
            }

            if (iqRequestHandler != null) {
               ExecutorService executorService = null;
               switch(iqRequestHandler.getMode()) {
               case sync:
                  executorService = this.singleThreadedExecutorService;
                  break;
               case async:
                  executorService = this.cachedExecutorService;
               }

               executorService.execute(new Runnable() {
                  public void run() {
                     IQ response = iqRequestHandler.handleIQRequest(iq);
                     if (response != null) {
                        try {
                           AbstractXMPPConnection.this.sendStanza(response);
                        } catch (SmackException.NotConnectedException | InterruptedException var3) {
                           AbstractXMPPConnection.LOGGER.log(Level.WARNING, "Exception while sending response to IQ request", var3);
                        }

                     }
                  }
               });
               return;
            }

            if (!this.replyToUnkownIq) {
               return;
            }

            ErrorIQ errorIQ = IQ.createErrorResponse(iq, new XMPPError(XMPPError.Condition.feature_not_implemented));

            try {
               this.sendStanza(errorIQ);
            } catch (SmackException.NotConnectedException | InterruptedException var11) {
               LOGGER.log(Level.WARNING, "Exception while sending error IQ to unkown IQ request", var11);
            }
         }
      }

      final Collection<StanzaListener> listenersToNotify = new LinkedList();
      Iterator i$;
      AbstractXMPPConnection.ListenerWrapper listenerWrapper;
      synchronized(this.asyncRecvListeners) {
         i$ = this.asyncRecvListeners.values().iterator();

         while(i$.hasNext()) {
            listenerWrapper = (AbstractXMPPConnection.ListenerWrapper)i$.next();
            if (listenerWrapper.filterMatches(packet)) {
               listenersToNotify.add(listenerWrapper.getListener());
            }
         }
      }

      Iterator i$ = listenersToNotify.iterator();

      while(i$.hasNext()) {
         final StanzaListener listener = (StanzaListener)i$.next();
         this.asyncGo(new Runnable() {
            public void run() {
               try {
                  listener.processPacket(packet);
               } catch (Exception var2) {
                  AbstractXMPPConnection.LOGGER.log(Level.SEVERE, "Exception in async packet listener", var2);
               }

            }
         });
      }

      i$ = this.collectors.iterator();

      while(i$.hasNext()) {
         PacketCollector collector = (PacketCollector)i$.next();
         collector.processPacket(packet);
      }

      listenersToNotify.clear();
      synchronized(this.syncRecvListeners) {
         i$ = this.syncRecvListeners.values().iterator();

         while(i$.hasNext()) {
            listenerWrapper = (AbstractXMPPConnection.ListenerWrapper)i$.next();
            if (listenerWrapper.filterMatches(packet)) {
               listenersToNotify.add(listenerWrapper.getListener());
            }
         }
      }

      this.singleThreadedExecutorService.execute(new Runnable() {
         public void run() {
            Iterator i$ = listenersToNotify.iterator();

            while(i$.hasNext()) {
               StanzaListener listener = (StanzaListener)i$.next();

               try {
                  listener.processPacket(packet);
               } catch (SmackException.NotConnectedException var4) {
                  AbstractXMPPConnection.LOGGER.log(Level.WARNING, "Got not connected exception, aborting", var4);
                  break;
               } catch (Exception var5) {
                  AbstractXMPPConnection.LOGGER.log(Level.SEVERE, "Exception in packet listener", var5);
               }
            }

         }
      });
   }

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

   }

   protected void callConnectionConnectedListener() {
      Iterator i$ = this.connectionListeners.iterator();

      while(i$.hasNext()) {
         ConnectionListener listener = (ConnectionListener)i$.next();
         listener.connected(this);
      }

   }

   protected void callConnectionAuthenticatedListener(boolean resumed) {
      Iterator i$ = this.connectionListeners.iterator();

      while(i$.hasNext()) {
         ConnectionListener listener = (ConnectionListener)i$.next();

         try {
            listener.authenticated(this, resumed);
         } catch (Exception var5) {
            LOGGER.log(Level.SEVERE, "Exception in authenticated listener", var5);
         }
      }

   }

   void callConnectionClosedListener() {
      Iterator i$ = this.connectionListeners.iterator();

      while(i$.hasNext()) {
         ConnectionListener listener = (ConnectionListener)i$.next();

         try {
            listener.connectionClosed();
         } catch (Exception var4) {
            LOGGER.log(Level.SEVERE, "Error in listener while closing connection", var4);
         }
      }

   }

   protected void callConnectionClosedOnErrorListener(Exception e) {
      LOGGER.log(Level.WARNING, "Connection closed with error", e);
      Iterator i$ = this.connectionListeners.iterator();

      while(i$.hasNext()) {
         ConnectionListener listener = (ConnectionListener)i$.next();

         try {
            listener.connectionClosedOnError(e);
         } catch (Exception var5) {
            LOGGER.log(Level.SEVERE, "Error in listener while closing connection", var5);
         }
      }

   }

   protected void notifyReconnection() {
      Iterator i$ = this.connectionListeners.iterator();

      while(i$.hasNext()) {
         ConnectionListener listener = (ConnectionListener)i$.next();

         try {
            listener.reconnectionSuccessful();
         } catch (Exception var4) {
            LOGGER.log(Level.WARNING, "notifyReconnection()", var4);
         }
      }

   }

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

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

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

   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 var5) {
         LOGGER.log(Level.WARNING, "finalize() threw trhowable", var5);
      } finally {
         super.finalize();
      }

   }

   protected final void parseFeatures(XmlPullParser parser) throws XmlPullParserException, IOException, SmackException, InterruptedException {
      this.streamFeatures.clear();
      int initialDepth = parser.getDepth();

      while(true) {
         while(true) {
            int eventType = parser.next();
            if (eventType == 2 && parser.getDepth() == initialDepth + 1) {
               ExtensionElement streamFeature = null;
               String name = parser.getName();
               String namespace = parser.getNamespace();
               byte var8 = -1;
               switch(name.hashCode()) {
               case -676919238:
                  if (name.equals("mechanisms")) {
                     var8 = 1;
                  }
                  break;
               case 3023933:
                  if (name.equals("bind")) {
                     var8 = 2;
                  }
                  break;
               case 1316817241:
                  if (name.equals("starttls")) {
                     var8 = 0;
                  }
                  break;
               case 1431984486:
                  if (name.equals("compression")) {
                     var8 = 4;
                  }
                  break;
               case 1984987798:
                  if (name.equals("session")) {
                     var8 = 3;
                  }
               }

               switch(var8) {
               case 0:
                  streamFeature = PacketParserUtils.parseStartTlsFeature(parser);
                  break;
               case 1:
                  streamFeature = new Mechanisms(PacketParserUtils.parseMechanisms(parser));
                  break;
               case 2:
                  streamFeature = Bind.Feature.INSTANCE;
                  break;
               case 3:
                  streamFeature = PacketParserUtils.parseSessionFeature(parser);
                  break;
               case 4:
                  streamFeature = PacketParserUtils.parseCompressionFeature(parser);
                  break;
               default:
                  ExtensionElementProvider<ExtensionElement> provider = ProviderManager.getStreamFeatureProvider(name, namespace);
                  if (provider != null) {
                     streamFeature = (ExtensionElement)provider.parse(parser);
                  }
               }

               if (streamFeature != null) {
                  this.addStreamFeature((ExtensionElement)streamFeature);
               }
            } else if (eventType == 3 && parser.getDepth() == initialDepth) {
               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();
               return;
            }
         }
      }
   }

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

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

   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);
   }

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

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

   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() {
         public void processPacket(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
            try {
               XMPPException.XMPPErrorException.ifHasErrorThenThrow(packet);
               callback.processPacket(packet);
            } catch (XMPPException.XMPPErrorException var6) {
               if (exceptionCallback != null) {
                  exceptionCallback.processException(var6);
               }
            } finally {
               AbstractXMPPConnection.this.removeAsyncStanzaListener(this);
            }

         }
      };
      this.removeCallbacksService.schedule(new Runnable() {
         public void run() {
            boolean removed = AbstractXMPPConnection.this.removeAsyncStanzaListener(packetListener);
            if (removed && exceptionCallback != null) {
               exceptionCallback.processException(SmackException.NoResponseException.newWith(AbstractXMPPConnection.this, (StanzaFilter)replyFilter));
            }

         }
      }, timeout, TimeUnit.MILLISECONDS);
      this.addAsyncStanzaListener(packetListener, replyFilter);
      this.sendStanza(stanza);
   }

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

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

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

   public void addOneTimeSyncCallback(final StanzaListener callback, StanzaFilter packetFilter) {
      final StanzaListener packetListener = new StanzaListener() {
         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() {
         public void run() {
            AbstractXMPPConnection.this.removeSyncStanzaListener(packetListener);
         }
      }, this.getPacketReplyTimeout(), TimeUnit.MILLISECONDS);
   }

   public IQRequestHandler registerIQRequestHandler(IQRequestHandler iqRequestHandler) {
      String key = XmppStringUtils.generateKey(iqRequestHandler.getElement(), iqRequestHandler.getNamespace());
      switch(iqRequestHandler.getType()) {
      case set:
         synchronized(this.setIqRequestHandler) {
            return (IQRequestHandler)this.setIqRequestHandler.put(key, iqRequestHandler);
         }
      case get:
         synchronized(this.getIqRequestHandler) {
            return (IQRequestHandler)this.getIqRequestHandler.put(key, iqRequestHandler);
         }
      default:
         throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed");
      }
   }

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

   public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) {
      String key = XmppStringUtils.generateKey(element, namespace);
      switch(type) {
      case set:
         synchronized(this.setIqRequestHandler) {
            return (IQRequestHandler)this.setIqRequestHandler.remove(key);
         }
      case get:
         synchronized(this.getIqRequestHandler) {
            return (IQRequestHandler)this.getIqRequestHandler.remove(key);
         }
      default:
         throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed");
      }
   }

   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;
      }

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