Package hudson.plugins.jabber.im.transport

Source Code of hudson.plugins.jabber.im.transport.JabberIMConnection$PrivateChatListener

/*
* Created on 06.03.2007
*/
package hudson.plugins.jabber.im.transport;

import hudson.Util;
import hudson.plugins.im.AbstractIMConnection;
import hudson.plugins.im.AuthenticationHolder;
import hudson.plugins.im.GroupChatIMMessageTarget;
import hudson.plugins.im.IMConnection;
import hudson.plugins.im.IMConnectionListener;
import hudson.plugins.im.IMException;
import hudson.plugins.im.IMMessageTarget;
import hudson.plugins.im.IMPresence;
import hudson.plugins.im.bot.Bot;
import hudson.plugins.im.tools.ExceptionHelper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.sasl.SaslException;

import org.apache.commons.io.IOUtils;
import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.RosterPacket.ItemType;
import org.jivesoftware.smack.packet.XMPPError.Condition;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.proxy.ProxyInfo.ProxyType;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.delay.packet.DelayInformation;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.nick.packet.Nick;
import org.jivesoftware.smackx.ping.packet.Ping;
import org.jivesoftware.smackx.vcardtemp.packet.VCard;
import org.springframework.util.Assert;

import sun.security.util.HostnameChecker;
import edu.umd.cs.findbugs.annotations.Nullable;

/**
* Smack-specific implementation of {@link IMConnection}.
*
* @author kutzi
* @author Uwe Schaefer (original author)
* @author jenky-hm
*/
class JabberIMConnection extends AbstractIMConnection {
 
  private static final Logger LOGGER = Logger.getLogger(JabberIMConnection.class.getName());
 
  private volatile XMPPConnection connection;

  private final Map<String, WeakReference<MultiUserChat>> groupChatCache = new HashMap<String, WeakReference<MultiUserChat>>();
  private final Map<String, WeakReference<Chat>> chatCache = new HashMap<String, WeakReference<Chat>>();
  private final Set<Bot> bots = new HashSet<Bot>();
  private final String passwd;
  private final String botCommandPrefix;
  /**
   * Jabber 'nick'. This is just the username-part of the Jabber-ID.
   * I.e. for 'john.doe@gmail.com' it is 'john.doe'.
   */
  private final String nick;
    /**
     * The 'resource' part of the Jabber ID.
     * I.e. for 'john.doe@gmail.com/Jenkins' it is 'Jenkins' or
     * for 'john.doe@gmail.com' it is null.
     */
  @Nullable
  private final String resource;
  /**
   * The nick name of the Jenkins bot to use in group chats.
   * May be null in which case the nick is used.
   */
  @Nullable
  private final String groupChatNick;
  /**
   * Server name of the Jabber server.
   */
  private final String hostnameOverride;
  private final int port;

  private final List<IMMessageTarget> groupChats;
 
  private IMPresence impresence;

    private String imStatusMessage;

    private final JabberPublisherDescriptor desc;
    private final AuthenticationHolder authentication;

  private Roster roster;
 
  /**
   * Proxy parameters
   */
  private final ProxyType proxytype;
  private final String proxyhost;
  private final String proxyuser;
  private final String proxypass;
  private final int proxyport;

  private final boolean acceptAllCerts;

  private Runnable keepAliveCommand;
 
  private ScheduledExecutorService scheduler;

  static {
    SmackConfiguration.setDefaultPacketReplyTimeout(20000);
   
    System.setProperty("smack.debuggerClass", JabberConnectionDebugger.class.getName());
  }
 
  JabberIMConnection(JabberPublisherDescriptor desc, AuthenticationHolder authentication) throws IMException {
      super(desc);
    Assert.notNull(desc, "Parameter 'desc' must not be null.");
    this.desc = desc;
    this.authentication = authentication;
    this.hostnameOverride = desc.getHostname();
    this.port = desc.getPort();
    this.nick = desc.getNickname();
    this.resource = StringUtils.parseResource(desc.getJabberId());
    this.passwd = desc.getPassword();
    this.proxytype = desc.getProxyType();
    this.proxyhost = desc.getProxyHost();
    this.proxyport = desc.getProxyPort();
    this.proxyuser = desc.getProxyUser();
    this.proxypass = desc.getProxyPass();
    this.groupChatNick = desc.getNickname();
    this.botCommandPrefix = desc.getCommandPrefix();
    this.groupChats = desc.getDefaultTargets();
    this.impresence = desc.isExposePresence() ? IMPresence.AVAILABLE : IMPresence.UNAVAILABLE;
    this.acceptAllCerts = desc.isAcceptAllCerts();
  }

  @Override
  public boolean connect() {
      lock();
      try {
      try {
        if (!isConnected()) {
          if (createConnection()) {
            LOGGER.info("Connected to XMPP on "
                + this.connection.getHost() + ":" + this.connection.getPort()
                + "/" + this.connection.getServiceName()
                + (this.connection.isSecureConnection() ? " using secure connection" : "")
                + (this.connection.isUsingCompression() ? " using compression" : ""));
     
            // kutzi: I've read somewhere that status must be set, before one can do anything other
            // Don't know if it's true, but can't hurt, either.
            sendPresence();
           
            groupChatCache.clear();
            for (IMMessageTarget chat : this.groupChats) {
                GroupChatIMMessageTarget groupChat = (GroupChatIMMessageTarget) chat;
              try {
                getOrCreateGroupChat(groupChat);
                LOGGER.info("Joined groupchat " + groupChat.getName());
              } catch (IMException e) {
                // if we got here, the XMPP connection could be established, but probably the groupchat name
                // is invalid
                LOGGER.warning("Unable to connect to groupchat '" + groupChat.getName() + "'. Did you append @conference or so to the name?\n"
                    + "Exception: " + ExceptionHelper.dump(e));
              }
            }
          } else {
            // clean-up if needed
            if (this.connection != null) {
              try {
                this.connection.disconnect();
              } catch (Exception e) {
                // ignore
              }
            }
            return false;
          }
        }
        return true;
      } catch (final Exception e) {
        LOGGER.warning(ExceptionHelper.dump(e));
        return false;
      }
    } finally {
        unlock();
    }
  }

  @Override
    public void close() {
      lock();
      try {
      try {
        for (WeakReference<MultiUserChat> entry : groupChatCache.values()) {
          MultiUserChat chat = entry.get();
          if (chat != null && chat.isJoined()) {
            chat.leave();
          }
        }
        // there seems to be no way to leave a 1-on-1 chat with Smack
       
        this.groupChatCache.clear();
        this.chatCache.clear();
       
        if (this.scheduler != null) {
          this.scheduler.shutdownNow();
          this.scheduler = null;
        }
       
        if (this.connection.isConnected()) {
          this.connection.disconnect();
        }
      } catch (Exception e) {
        // ignore
        LOGGER.fine(e.toString());
      } finally {
        this.connection = null;
      }
    } finally {
        unlock();
    }
  }

  private boolean createConnection() throws XMPPException, SaslException,
      SmackException, IOException, NoSuchAlgorithmException,
      KeyManagementException {
    if (this.connection != null) {
      try {
        this.connection.disconnect();
      } catch (Exception ignore) {
        // ignore
      }
    }
   
    ProxyInfo pi;
    switch (this.proxytype) {
      case HTTP:
        pi = ProxyInfo.forHttpProxy(this.proxyhost, this.proxyport, this.proxyuser, this.proxypass);
        break;
      case SOCKS4:
        pi = ProxyInfo.forSocks4Proxy(this.proxyhost, this.proxyport, this.proxyuser, this.proxypass);
        break;
      case SOCKS5:
        pi = ProxyInfo.forSocks5Proxy(this.proxyhost, this.proxyport, this.proxyuser, this.proxypass);
        break;
      default:
        pi = ProxyInfo.forNoProxy();
        break;
    }
   
    String serviceName = desc.getServiceName();
    final ConnectionConfiguration cfg;
    if (serviceName == null) {
      cfg = new ConnectionConfiguration(
          this.hostnameOverride, this.port, pi);
    } else if (this.hostnameOverride == null) {
        // uses DNS lookup, to get the actual hostname for this service:
      cfg = new ConnectionConfiguration(serviceName, pi);
    } else {
      cfg = new ConnectionConfiguration(
          this.hostnameOverride, this.port,
          serviceName, pi);
    }
    // Currently, we handle reconnects ourself.
    // Maybe we should change it in the future, but currently I'm
    // not sure what Smack's reconnect feature really does.
    cfg.setReconnectionAllowed(false);
   
    cfg.setDebuggerEnabled(true);

    if (acceptAllCerts) {
      // TODO Smack 4.1 provides TLSUtil.acceptAllCertificates, replace
      // the code here with the code provided by Smack
      SSLContext context = SSLContext.getInstance("TLS");
      // Install an "accept all" trust manager
      X509TrustManager tm = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] arg0,
            String arg1) throws CertificateException {
          // Nothing to do here
        }
        @Override
        public void checkServerTrusted(X509Certificate[] arg0,
            String arg1) throws CertificateException {
          // Nothing to do here
        }
        @Override
        public X509Certificate[] getAcceptedIssuers() {
          return new X509Certificate[0];
        }
      };
      context.init(null, new TrustManager[] { tm }, new SecureRandom());
      cfg.setCustomSSLContext(context);
      cfg.setHostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String arg0, SSLSession arg1) {
          return true;
        }
      });
    } else {
      // TODO This hostname verifier is the default in Smack 4.1 when
      // smack-java7 is used, remove the code once Smack 4.1 is used
      cfg.setHostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
          HostnameChecker checker = HostnameChecker
              .getInstance(HostnameChecker.TYPE_TLS);

          boolean validCertificate = false, validPrincipal = false;
          try {
            Certificate[] peerCertificates = session
                .getPeerCertificates();

            if (peerCertificates.length > 0
                && peerCertificates[0] instanceof X509Certificate) {
              X509Certificate peerCertificate = (X509Certificate) peerCertificates[0];

              try {
                checker.match(hostname, peerCertificate);
                // Certificate matches hostname
                validCertificate = true;
              } catch (CertificateException ex) {
                // Certificate does not match hostname
              }
            } else {
              // Peer does not have any certificates or they
              // aren't X.509
            }
          } catch (SSLPeerUnverifiedException ex) {
            // Not using certificates for peers, try verifying the
            // principal
            try {
              Principal peerPrincipal = session
                  .getPeerPrincipal();
              if (peerPrincipal instanceof KerberosPrincipal) {
                validPrincipal = HostnameChecker.match(
                    hostname,
                    (KerberosPrincipal) peerPrincipal);
              } else {
                // Can't verify principal, not Kerberos
              }
            } catch (SSLPeerUnverifiedException ex2) {
              // Can't verify principal, no principal
            }
          }

          return validCertificate || validPrincipal;
        }
      });
    }

        LOGGER.info("Trying to connect to XMPP on "
                + "/" + cfg.getServiceName()
                + (cfg.isCompressionEnabled() ? " using compression" : "")
                + (pi.getProxyType() != ProxyInfo.ProxyType.NONE ? " via proxy " + pi.getProxyType() + " "
                        + pi.getProxyAddress() + ":" + pi.getProxyPort() : "")
                );

        boolean retryWithLegacySSL = false;
        XMPPException originalException = null;
    try {
      this.connection = new XMPPTCPConnection(cfg);
      this.connection.connect();
      if (!this.connection.isConnected()) {
        retryWithLegacySSL = true;
      }
    } catch (XMPPException e) {
      retryWithLegacySSL = true;
      originalException = e;
    } catch (SmackException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        } catch (IOException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        }

        if (retryWithLegacySSL) {
      retryConnectionWithLegacySSL(cfg, originalException);
    }

    if (this.connection.isConnected()) {
      this.connection.login(this.desc.getUserName(), this.passwd,
        this.resource != null ? this.resource : "Jenkins");
     
      setupSubscriptionMode();
      createVCardIfNeeded();
     
      installServerTypeHacks();
     
      listenForPrivateChats();
    }
   
    return this.connection.isAuthenticated();
  }

  private void installServerTypeHacks() {
    if (this.connection.getServiceName().contains("hipchat")) {
      // JENKINS-25222: HipChat connections time out after 150 seconds (http://help.hipchat.com/knowledgebase/articles/64377-xmpp-jabber-support-details)
      addConnectionKeepAlivePings();
    }
  }

  private void addConnectionKeepAlivePings() {
    if (this.scheduler == null)  {
      this.scheduler = Executors.newScheduledThreadPool(1);
    }
   
    if (keepAliveCommand != null) {
      return;
    }
   
    keepAliveCommand = new Runnable() {
     
      @Override
      public void run() {
        // prevent long waits for lock
            try {
          if (!tryLock(5, TimeUnit.SECONDS)) {
              return;
          }
         
          try {
            connection.sendPacket(new Ping());
          } catch (NotConnectedException e) {
            // connection died, so lets scheduled task die, too
            throw new RuntimeException(e);
          } finally {
            unlock();
          }
         
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      }
    };
    scheduler.scheduleAtFixedRate(keepAliveCommand, 60, 60, TimeUnit.SECONDS);
  }

  /**
   * Transparently retries the connection attempt with legacy SSL if original attempt fails.
   * @param originalException the exception of the original attempt (may be null)
   *
   * See JENKINS-6863
   */
  private void retryConnectionWithLegacySSL(
      final ConnectionConfiguration cfg, XMPPException originalException)
      throws XMPPException, SmackException {
    try {
      LOGGER.info("Retrying connection with legacy SSL");
      cfg.setSocketFactory(SSLSocketFactory.getDefault());
      this.connection = new XMPPTCPConnection(cfg);
      this.connection.connect();
    } catch (XMPPException e) {
      if (originalException != null) {
        // use the original connection exception as legacy SSL should only
        // be a fallback
          LOGGER.warning("Retrying with legacy SSL failed: " + e.getMessage());
        throw new SmackException("Exception of original (without legacy SSL) connection attempt", originalException);
      } else {
        throw new SmackException(e);
      }
    } catch (SmackException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        } catch (IOException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        }
    }

  /**
   * Sets the chosen subscription mode on our connection.
   */
  private void setupSubscriptionMode() {
    this.roster = this.connection.getRoster();
    SubscriptionMode mode = SubscriptionMode.valueOf(this.desc.getSubscriptionMode());
    switch (mode) {
      case accept_all : LOGGER.info("Accepting all subscription requests");
        break;
      case reject_all : LOGGER.info("Rejecting all subscription requests");
        break;
      case manual : LOGGER.info("Subscription requests must be handled manually");
        break;
    }
    this.roster.setSubscriptionMode(mode);
  }

  /** Sets the Jenkins vCard avatar for this account, if not done so already. */
  private void createVCardIfNeeded() {
      try {
          if (!vCardExists()) {
              createVCard();
          }
    } catch (XMPPException e) {
      LOGGER.warning(ExceptionHelper.dump(e));
    } catch (SmackException.NotConnectedException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        } catch (SmackException.NoResponseException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        }
    }
 
  // Unfortunately the Smack API doesn't specify what concretely happens, if a vCard doesn't exist, yet.
  // It could be just an empty vCard or an XMPPException thrown.
  private boolean vCardExists() throws XMPPException {
      try {
            VCard vcard = new VCard();
            vcard.load(this.connection);
           
            // Best effort check to see if the vcard already exists.
            if (Util.fixEmpty(vcard.getNickName()) != null) {
                return true;
            }
            return false;
        } catch (XMPPException e) {
            if (e instanceof  XMPPException.XMPPErrorException){
                XMPPException.XMPPErrorException ex =(XMPPException.XMPPErrorException) e;
                // See http://xmpp.org/extensions/xep-0054.html#sect-id304495
                if (ex.getXMPPError() != null && Condition.item_not_found.toString().equals(ex.getXMPPError().getCondition())) {
                    return false;
                }
            }
           
            // there was probably a 'real' problem
            throw e;
        } catch (SmackException.NotConnectedException | SmackException.NoResponseException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
            return false;
        } catch (ClassCastException e) {
          // This seems to be a VCard parsing exception in Smack 4.0.x
          // See e.g. http://stackoverflow.com/questions/26752285/android-asmack-vcard-classcastexception-while-calling-vcard-loadconn
          LOGGER.warning(ExceptionHelper.dump(e));
          // Assume vcard exists, just couldn't be parsed by smack
          return true;
        }
    }

  /**
   * Constructs a vCard for Mr Jenkins.
   */
  private void createVCard() throws XMPPException, SmackException.NotConnectedException, SmackException.NoResponseException {

    VCard vCard = new VCard();
    vCard.setFirstName("Mr.");
    vCard.setLastName("Jenkins");
    vCard.setNickName(this.nick);
    setAvatarImage(vCard);
    vCard.save(this.connection);
  }
 
  private void setAvatarImage(VCard vCard) {
        InputStream input = null;
        ByteArrayOutputStream output = null;
        try {
            input = JabberIMConnection.class.getResourceAsStream("headshot.png");
            output = new ByteArrayOutputStream();
            IOUtils.copy(input, output);
            byte[] avatar = output.toByteArray();
            vCard.setAvatar(avatar, "image/png");
        } catch (IOException e) {
            LOGGER.warning(ExceptionHelper.dump(e));
        } finally {
            IOUtils.closeQuietly(input);
            IOUtils.closeQuietly(output);
        }
    }

    /**
   * Listens on the connection for private chat requests.
   */
  private void listenForPrivateChats() {
    //PacketFilter filter = new AndFilter(new MessageTypeFilter(Message.Type.chat),
    //    new ToContainsFilter(this.desc.getUserName()));
    // Actually, this should be the full user name (including '@server')
    // but since via this connection only message to me should be delivered (right?)
    // this doesn't matter anyway.


        // TODO: ToContainsFilter which was in Smack 4.0.0!?
        PacketFilter filter = new MessageTypeFilter(Message.Type.chat);

    PacketListener listener = new PrivateChatListener();
    this.connection.addPacketListener(listener, filter);
  }

  private MultiUserChat getOrCreateGroupChat(GroupChatIMMessageTarget chat) throws IMException {
    WeakReference<MultiUserChat> ref = groupChatCache.get(chat.getName());
    MultiUserChat groupChat = null;
    if (ref != null) {
      groupChat = ref.get();
    }
   
    if (groupChat == null) {
      groupChat = new MultiUserChat(this.connection, chat.getName());
      try {
        groupChat.join(this.groupChatNick, chat.getPassword());
      } catch (XMPPException e) {
        LOGGER.warning("Cannot join group chat '" + chat + "'. Exception:\n" + ExceptionHelper.dump(e));
        throw new IMException(e);
      } catch (SmackException e) {
                LOGGER.warning("Cannot join group chat '" + chat + "'. Exception:\n" + ExceptionHelper.dump(e));
                throw new IMException(e);
            }

      // get rid of old messages:
      while (groupChat.pollMessage() != null) {
      }

      this.bots.add(new Bot(new JabberMultiUserChat(groupChat, this, !chat.isNotificationOnly()),
          this.groupChatNick, this.desc.getHost(),
          this.botCommandPrefix, this.authentication));

      groupChatCache.put(chat.getName(), new WeakReference<MultiUserChat>(groupChat));
    }
    return groupChat;
  }
 
  private Chat getOrCreatePrivateChat(String chatPartner, Message msg) {
    // use possibly existing chat
    WeakReference<Chat> wr = chatCache.get(chatPartner);
    if (wr != null) {
      Chat c = wr.get();
      if (c != null) {
        return c;
      }
    }
   
    final Chat chat = ChatManager.getInstanceFor(this.connection).createChat(chatPartner, null);
    Bot bot = new Bot(new JabberChat(chat, this), this.groupChatNick,
          this.desc.getHost(), this.botCommandPrefix, this.authentication);
    this.bots.add(bot);
   
    if (msg != null) {
      // replay original message:
      bot.onMessage(new JabberMessage(msg, isAuthorized(msg.getFrom())));
    }
    chatCache.put(chatPartner, new WeakReference<Chat>(chat));
    return chat;
  }

  public void send(final IMMessageTarget target, final String text)
      throws IMException {
    Assert.notNull(target, "Parameter 'target' must not be null.");
    Assert.notNull(text, "Parameter 'text' must not be null.");
    try {
        // prevent long waits for lock
            if (!tryLock(5, TimeUnit.SECONDS)) {
                return;
            }
            try {
                if (target instanceof GroupChatIMMessageTarget) {
                  getOrCreateGroupChat((GroupChatIMMessageTarget) target).sendMessage(
                      text);
                } else {
                  final Chat chat = getOrCreatePrivateChat(target.toString(), null);
                  chat.sendMessage(text);
                }
            } catch (final XMPPException e) {
              // server unavailable ? Target-host unknown ? Well. Just skip this
              // one.
                LOGGER.warning(ExceptionHelper.dump(e));
              // TODO ? tryReconnect();
            } catch (SmackException.NotConnectedException e) {
                LOGGER.warning(ExceptionHelper.dump(e));
            } finally {
                unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            // ignore
        }
  }

  /**
   * This implementation ignores the new presence if
   * {@link JabberPublisherDescriptor#isExposePresence()} is false.
   */
  @Override
  public void setPresence(final IMPresence impresence, String statusMessage)
      throws IMException {
    Assert.notNull(impresence, "Parameter 'impresence' must not be null.");
    if (this.desc.isExposePresence()) {
        this.impresence = impresence;
        this.imStatusMessage = statusMessage;
        sendPresence();
    } else {
        // Ignore new presence.
       
        // Don't re-send presence, either. It would result in disconnecting from
        // all joined group chats
    }
  }
 
  private void sendPresence() {
     
      try {
          // prevent long waits for lock
            if (!tryLock(5, TimeUnit.SECONDS)) {
                return;
            }
            try {
              if( !isConnected() ) {
                return;
              }
              Presence presence;
              switch (this.impresence) {
              case AVAILABLE:
                presence = new Presence(Presence.Type.available,
                    this.imStatusMessage, 1, Presence.Mode.available);
                break;
             
              case OCCUPIED:
                  presence = new Presence(Presence.Type.available,
                          this.imStatusMessage, 1, Presence.Mode.away);
                  break;
                 
              case DND:
                  presence = new Presence(Presence.Type.available,
                            this.imStatusMessage, 1, Presence.Mode.dnd);
                    break;

              case UNAVAILABLE:
                presence = new Presence(Presence.Type.unavailable);
                break;

              default:
                throw new IllegalStateException("Don't know how to handle "
                    + impresence);
              }
             
              presence.addExtension(new Nick(this.nick));
              this.connection.sendPacket(presence);
            } catch (SmackException.NotConnectedException e) {
                LOGGER.warning(ExceptionHelper.dump(e));
            } finally {
                unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            // ignore
        }
  }
 
  @Override
    public boolean isConnected() {
      lock();
    try {
      return this.connection != null && this.connection.isAuthenticated();
    } finally {
        unlock();
    }
  }
 
  public boolean isAuthorized(String xmppAddress) {
    String bareAddress = StringUtils.parseBareAddress(xmppAddress);
   
    // is this a (private) message send from a user in a chat I'm part of?
    boolean authorized = this.groupChatCache.containsKey(bareAddress);
   
    if (!authorized) {
        RosterEntry entry = this.roster.getEntry(bareAddress);
            authorized = entry != null
              && (entry.getType() == ItemType.both
                  || entry.getType() == ItemType.from);
    }
       
        return authorized;
  }
 
  private final Map<IMConnectionListener, ConnectionListener> listeners =
    new ConcurrentHashMap<IMConnectionListener, ConnectionListener>();
 
  @Override
  public void addConnectionListener(final IMConnectionListener listener) {
    lock();
    try {
      ConnectionListener l = new AbstractConnectionListener() {
        @Override
        public void connectionClosedOnError(Exception e) {
          listener.connectionBroken(e);
        }
      };
      listeners.put(listener, l);
      this.connection.addConnectionListener(l);
    } finally {
      unlock();
    }
  }

  @Override
  public void removeConnectionListener(IMConnectionListener listener) {
    lock();
    try {
      ConnectionListener l = this.listeners.remove(listener);
      if (l != null) {
        this.connection.removeConnectionListener(l);
      } else {
        LOGGER.warning("Connection listener " + listener + " not found.");
      }
    } finally {
      unlock();
    }
  }

  /**
   * Listens for private chats.
   */
  private final class PrivateChatListener implements PacketListener {

    public void processPacket(Packet packet) {
      if (packet instanceof Message) {
        Message m = (Message)packet;

        for (PacketExtension ext : m.getExtensions()) {
          if (ext instanceof DelayInformation) {
            // ignore delayed messages
            return;
          }
        }
               
        if (m.getBody() != null) {
          LOGGER.fine("Message from " + m.getFrom() + " : " + m.getBody());
         
          final String chatPartner = m.getFrom();
          getOrCreatePrivateChat(chatPartner, m);
        }
      }
    }
  };
}
TOP

Related Classes of hudson.plugins.jabber.im.transport.JabberIMConnection$PrivateChatListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.