// --------------------------------------------------------------------------- // jWebSocket - jWebSocket XMPP/Jabber Plug-In // Copyright (c) 2010 Innotrade GmbH, jWebSocket.org // --------------------------------------------------------------------------- // THIS CODE IS FOR RESEARCH, EVALUATION AND TEST PURPOSES ONLY! // THIS CODE MAY BE SUBJECT TO CHANGES WITHOUT ANY NOTIFICATION! // --------------------------------------------------------------------------- // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 3 of the License, or (at your // option) any later version. // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for // more details. // You should have received a copy of the GNU Lesser General Public License along // with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>. // --------------------------------------------------------------------------- package org.jwebsocket.plugins.xmpp; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import javolution.util.FastList; import javolution.util.FastMap; import org.apache.log4j.Logger; import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ChatManager; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.RosterPacket.ItemStatus; import org.jivesoftware.smack.packet.RosterPacket.ItemType; import org.jwebsocket.api.PluginConfiguration; import org.jwebsocket.api.WebSocketConnector; import org.jwebsocket.config.JWebSocketServerConstants; import org.jwebsocket.kit.PlugInResponse; import org.jwebsocket.logging.Logging; import org.jwebsocket.plugins.TokenPlugIn; import org.jwebsocket.server.TokenServer; import org.jwebsocket.token.BaseToken; import org.jwebsocket.token.Token; import org.jwebsocket.token.TokenFactory; /** * * @author aschulze * * This Plug-In make heavy use of Smack API 3.1.0 * http://www.igniterealtime.org/projects/smack/ * * Nice tips at: http://www.adarshr.com/papers/xmpp and http://www.adarshr.com/papers/xmpp2 * Smack Java Docs: http://www.igniterealtime.org/builds/smack/docs/latest/javadoc/ * */ public class XMPPPlugIn extends TokenPlugIn { private static Logger mLog = Logging.getLogger(XMPPPlugIn.class); private static final String XMPP_CONN_VAR = "$xmpp_connection"; private static final String XMPP_CHAT_VAR = "$xmpp_chat"; private static final String XMPP_CRED_VAR = "$xmpp_credentials"; // if namespace changed update client plug-in accordingly! private static final String NS_XMPP = JWebSocketServerConstants.NS_BASE + ".plugins.xmpp"; public XMPPPlugIn() { super(null); } public XMPPPlugIn(PluginConfiguration aConfiguration) { super(aConfiguration); if (mLog.isDebugEnabled()) { mLog.debug("Instantiating XMPP plug-in..."); } // specify default name space for xmpp plugin this.setNamespace(NS_XMPP); mGetSettings(); } private void mGetSettings() { } @Override public void processToken(PlugInResponse aResponse, WebSocketConnector aConnector, Token aToken) { String lType = aToken.getType(); String lNS = aToken.getNS(); if (lType != null && getNamespace().equals(lNS)) { if (lType.equals("connect")) { connect(aConnector, aToken); } else if (lType.equals("login")) { login(aConnector, aToken); } else if (lType.equals("logout")) { logout(aConnector, aToken); } else if (lType.equals("disconnect")) { disconnect(aConnector, aToken); } else if (lType.equals("getRoster")) { getRoster(aConnector, aToken); } else if (lType.equals("setPresence")) { setPresence(aConnector, aToken); } else if (lType.equals("openChat")) { openChat(aConnector, aToken); } else if (lType.equals("sendChat")) { sendChat(aConnector, aToken); } else if (lType.equals("closeChat")) { closeChat(aConnector, aToken); } } } private XMPPConnection getXMPPConnection(WebSocketConnector aConnector) { return (XMPPConnection) aConnector.getVar(XMPP_CONN_VAR); } private void setXMPPConnection(WebSocketConnector aConnector, XMPPConnection aConnection) { aConnector.setVar(XMPP_CONN_VAR, aConnection); } private void removeXMPPConnection(WebSocketConnector aConnector) { aConnector.removeVar(XMPP_CONN_VAR); } private class Credentials { private String mUsername = null; private String mPassword = null; private String mHost = null; private Integer mPort = null; private String mDomain = null; private Boolean mUseSSL = null; public Credentials(String aUsername, String aPassword, String aHost, Integer aPort, String aDomain, Boolean aUseSSL) { mUsername = aUsername; mPassword = aPassword; mHost = aHost; mPort = aPort; mDomain = aDomain; mUseSSL = aUseSSL; } public Credentials(String aHost, Integer aPort, String aDomain, Boolean aUseSSL) { mHost = aHost; mPort = aPort; mDomain = aDomain; mUseSSL = aUseSSL; } public Credentials(String aUsername, String aPassword) { mUsername = aUsername; mPassword = aPassword; } public boolean validateConnection() { return (mHost != null && mHost.length() > 0) && (mPort != null) && (mUseSSL != null); } public boolean validateUser() { return (mUsername != null && mUsername.length() > 0) && (mPassword != null && mPassword.length() > 0); } /** * @return the mUsername */ public String getUsername() { return mUsername; } /** * @return the mPassword */ public String getPassword() { return mPassword; } /** * @return the mHost */ public String getHost() { return mHost; } /** * @return the mPort */ public Integer getPort() { return mPort; } /** * @return the mUseSSL */ public Boolean getUseSSL() { return mUseSSL; } /** * @param aUsername the mUsername to set */ public void setUsername(String aUsername) { this.mUsername = aUsername; } /** * @param aPassword the mPassword to set */ public void setPassword(String aPassword) { this.mPassword = aPassword; } /** * @param aHost the mHost to set */ public void setHost(String aHost) { this.mHost = aHost; } /** * @param aPort the mPort to set */ public void setPort(Integer aPort) { this.mPort = aPort; } /** * @param aUseSSL the mUseSSL to set */ public void setUseSSL(Boolean aUseSSL) { this.mUseSSL = aUseSSL; } /** * @return the mDomain */ public String getDomain() { return mDomain; } /** * @param mDomain the mDomain to set */ public void setmDomain(String mDomain) { this.mDomain = mDomain; } } private Credentials getCredentials(WebSocketConnector aConnector) { return (Credentials) aConnector.getVar(XMPP_CRED_VAR); } private void setCredentials(WebSocketConnector aConnector, Credentials aCredentials) { aConnector.setVar(XMPP_CRED_VAR, aCredentials); } private void removeCredentials(WebSocketConnector aConnector) { aConnector.removeVar(XMPP_CRED_VAR); } private void connect(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; Credentials lCredentials = new Credentials( aToken.getString("host"), aToken.getInteger("port"), aToken.getString("domain"), aToken.getBoolean("useSSL")); if (!lCredentials.validateConnection()) { lMsg = "Invalid or incomplete connection data for XMPP server."; mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); // send response to requester lServer.sendToken(aConnector, lResponse); return; } try { // check if already connected to the same or a different server. XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lXMPPConn == null) { // Create a connection to the xmpp server on a specific port. ConnectionConfiguration lConnCfg = new ConnectionConfiguration( lCredentials.getHost(), lCredentials.getPort(), lCredentials.getDomain()); // especially for google talk! lConnCfg.setSASLAuthenticationEnabled(false); lXMPPConn = new XMPPConnection(lConnCfg); } if (!lXMPPConn.isConnected()) { lXMPPConn.connect(); } lMsg = "Successfully connected to XMPP server"; lResponse.setString("msg", lMsg); if (mLog.isInfoEnabled()) { mLog.info(lMsg); } setXMPPConnection(aConnector, lXMPPConn); } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); removeXMPPConnection(aConnector); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void disconnect(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; try { // check if already connected to the same or a different server. XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lXMPPConn != null && lXMPPConn.isConnected()) { lXMPPConn.disconnect(); lMsg = "Successfully disconnected from XMPP server"; } else { lResponse.setInteger("code", -1); lMsg = "Not connected to XMPP server"; } if (mLog.isInfoEnabled()) { mLog.info(lMsg); } lResponse.setString("msg", lMsg); } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } removeXMPPConnection(aConnector); // send response to requester lServer.sendToken(aConnector, lResponse); } private void login(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; String lUsername = aToken.getString("username"); String lPassword = aToken.getString("password"); try { XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lUsername == null || lUsername.length() <= 0) { lMsg = "No username passed for XMPP login."; if (mLog.isDebugEnabled()) { mLog.debug(lMsg); } lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } else if (lPassword == null || lPassword.length() <= 0) { lMsg = "No password passed for XMPP login."; if (mLog.isDebugEnabled()) { mLog.debug(lMsg); } lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } else if (lXMPPConn == null || !lXMPPConn.isConnected()) { lMsg = "Please connect first before trying to log in."; if (mLog.isDebugEnabled()) { mLog.debug(lMsg); } lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } else if (lXMPPConn != null && lXMPPConn.isAuthenticated()) { lMsg = "You are already logged in, logout and re-login to change account."; if (mLog.isDebugEnabled()) { mLog.debug(lMsg); } lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } else { lXMPPConn.login(lUsername, lPassword, "jWebSocket_" + new Date().getTime()); lMsg = "Successfully authenticated against XMPP server."; lResponse.setString("msg", lMsg); if (mLog.isInfoEnabled()) { mLog.info(lMsg); } } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void logout(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; try { // check if already connected to the same or a different server. XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lXMPPConn != null && lXMPPConn.isConnected()) { // Create a new presence. Pass in false to indicate we're unavailable. Presence lPresence = new Presence(Presence.Type.unavailable); lPresence.setPriority(24); // send the update lXMPPConn.sendPacket(lPresence); lMsg = "Successfully logged out from XMPP server"; } else { lResponse.setInteger("code", -1); lMsg = "Not logged in into XMPP server"; } if (mLog.isInfoEnabled()) { mLog.info(lMsg); } lResponse.setString("msg", lMsg); } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } removeXMPPConnection(aConnector); // send response to requester lServer.sendToken(aConnector, lResponse); } private void getRoster(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; try { // check if already connected to the same or a different server. XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lXMPPConn != null && lXMPPConn.isConnected()) { Roster lRoster = lXMPPConn.getRoster(); if (lRoster != null) { Collection<RosterEntry> lEntries = lRoster.getEntries(); // list os all roster items List lItems = new FastList(); for (RosterEntry lEntry : lEntries) { // map of all fields for a roster item Map lItem = new FastMap(); String lString; lString = lEntry.getName(); lItem.put("name", (lString != null ? lString : "[unknown]")); ItemStatus lStatus = lEntry.getStatus(); lItem.put("status", (lStatus != null ? lStatus.toString() : "[unknown]")); lString = lEntry.getUser(); lItem.put("user", (lString != null ? lString : "[unknown]")); ItemType lType = lEntry.getType(); lItem.put("type", (lType != null ? lType.name() : "[unknown]")); lItems.add(lItem); } lResponse.setList("roster", lItems); lMsg = "Roster successfully received."; } else { lResponse.setInteger("code", -1); lMsg = "Roster could not be obtained."; } } else { lResponse.setInteger("code", -1); lMsg = "Not connected or logged in into XMPP server"; } if (mLog.isInfoEnabled()) { mLog.info(lMsg); } lResponse.setString("msg", lMsg); } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void setPresence(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; String lStatus = aToken.getString("pstatus"); String lType = aToken.getString("ptype"); String lMode = aToken.getString("pmode"); Integer lPriority = aToken.getInteger("ppriority"); try { // check if already connected to the same or a different server. XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lXMPPConn != null && lXMPPConn.isConnected()) { // Create a new presence. Pass in false to indicate we're unavailable. Presence lPresence = new Presence( Presence.Type.valueOf(lType), lStatus, lPriority, Presence.Mode.valueOf(lMode)); lXMPPConn.sendPacket(lPresence); lMsg = "Status successfully sent to '" + lStatus + "'."; } else { lResponse.setInteger("code", -1); lMsg = "No XMPP connection or authentication."; } if (mLog.isInfoEnabled()) { mLog.info(lMsg); } lResponse.setString("msg", lMsg); } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private class XMPPMessageListener implements MessageListener { TokenServer mServer = null; WebSocketConnector mConnector = null; public XMPPMessageListener(WebSocketConnector aConnector, TokenServer aServer) { mServer = aServer; mConnector = aConnector; } @Override public void processMessage(Chat aChat, Message aMessage) { Token lToken = TokenFactory.createToken(NS_XMPP, BaseToken.TT_EVENT); lToken.setString("name", "chatMessage"); lToken.setString("from", aMessage.getFrom()); lToken.setString("to", aMessage.getTo()); lToken.setString("body", aMessage.getBody()); mServer.sendToken(mConnector, lToken); } } private void openChat(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; // example: "jsmith@jivesoftware.com" String lUserId = aToken.getString("userId"); try { // check if already connected to the same or a different server. XMPPConnection lXMPPConn = getXMPPConnection(aConnector); if (lXMPPConn != null && lXMPPConn.isConnected()) { // Assume we've created an XMPPConnection name "connection". ChatManager lChatmanager = lXMPPConn.getChatManager(); Chat lChat = lChatmanager.createChat(lUserId, new XMPPMessageListener(aConnector, lServer)); aConnector.setVar(XMPP_CHAT_VAR, lChat); lMsg = "Chat successfully opened."; } else { lResponse.setInteger("code", -1); lMsg = "No XMPP connection or authentication."; } if (mLog.isInfoEnabled()) { mLog.info(lMsg); } lResponse.setString("msg", lMsg); } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void sendChat(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; // example: "jsmith@jivesoftware.com" String lUserId = aToken.getString("userId"); String lMessage = aToken.getString("message"); try { Chat lChat = (Chat) aConnector.getVar(XMPP_CHAT_VAR); if (lChat != null) { lChat.sendMessage(lMessage); lMsg = "Message successfully sent to '" + lUserId + "'."; } else { lResponse.setInteger("code", -1); lMsg = "No chat active."; } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void closeChat(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; // example: "jsmith@jivesoftware.com" String lUserId = aToken.getString("userId"); try { Chat lChat = (Chat) aConnector.getVar(XMPP_CHAT_VAR); if (lChat != null) { // lChat. lMsg = "Chat with '" + lUserId + "' closed."; } else { lResponse.setInteger("code", -1); lMsg = "No chat active."; } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } }