/* * Copyright (C) 2010 Moduad Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.androidpn.server.xmpp.session; import org.androidpn.server.service.UserNotFoundException; import org.androidpn.server.xmpp.auth.AuthToken; import org.androidpn.server.xmpp.net.Connection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmpp.packet.JID; import org.xmpp.packet.Presence; /** * This class represents a session between the server and a client. * * @author Sehwan Noh (devnoh@gmail.com) */ public class ClientSession extends Session { private static final Log log = LogFactory.getLog(ClientSession.class); private static final String ETHERX_NAMESPACE = "http://etherx.jabber.org/streams"; private AuthToken authToken; private boolean initialized; private boolean wasAvailable = false; private Presence presence = null; /** * Constructor. * * @param serverName the server name * @param connection the connection * @param streamID the stream ID */ public ClientSession(String serverName, Connection connection, String streamID) { super(serverName, connection, streamID); presence = new Presence(); presence.setType(Presence.Type.unavailable); } /** * Creates a new session between the server and a client, and returns it. * * @param serverName the server name * @param connection the connection * @param xpp the XML parser to handle incoming data * @return a newly created session * @throws XmlPullParserException if an error occurs while parsing incoming data */ public static ClientSession createSession(String serverName, Connection connection, XmlPullParser xpp) throws XmlPullParserException { log.debug("createSession()..."); if (!xpp.getName().equals("stream")) { throw new XmlPullParserException("Bad opening tag (not stream)"); } if (!xpp.getNamespace(xpp.getPrefix()).equals(ETHERX_NAMESPACE)) { throw new XmlPullParserException("Stream not in correct namespace"); } String language = "en"; for (int i = 0; i < xpp.getAttributeCount(); i++) { if ("lang".equals(xpp.getAttributeName(i))) { language = xpp.getAttributeValue(i); } } // Store language and version information connection.setLanaguage(language); connection.setXMPPVersion(MAJOR_VERSION, MINOR_VERSION); // Create a ClientSession ClientSession session = SessionManager.getInstance() .createClientSession(connection); // Build the start packet response StringBuilder sb = new StringBuilder(200); sb.append("<?xml version='1.0' encoding='UTF-8'?>"); sb.append("<stream:stream "); sb .append("xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\" from=\""); sb.append(serverName); sb.append("\" id=\""); sb.append(session.getStreamID().toString()); sb.append("\" xml:lang=\""); sb.append(language); sb.append("\" version=\""); sb.append(MAJOR_VERSION).append(".").append(MINOR_VERSION); sb.append("\">"); connection.deliverRawText(sb.toString()); // XMPP 1.0 needs stream features sb = new StringBuilder(); sb.append("<stream:features>"); if (connection.getTlsPolicy() != Connection.TLSPolicy.disabled) { sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">"); if (connection.getTlsPolicy() == Connection.TLSPolicy.required) { sb.append("<required/>"); } sb.append("</starttls>"); } String specificFeatures = session.getAvailableStreamFeatures(); if (specificFeatures != null) { sb.append(specificFeatures); } sb.append("</stream:features>"); connection.deliverRawText(sb.toString()); return session; } /** * Returns the username associated with this session. * * @return the username * @throws UserNotFoundException if a user has not authenticated yet */ public String getUsername() throws UserNotFoundException { if (authToken == null) { throw new UserNotFoundException(); } return getAddress().getNode(); } /** * Returns the authentication token associated with this session. * * @return the authentication token */ public AuthToken getAuthToken() { return authToken; } /** * Initialize the session with an authentication token * @param authToken the authentication token */ public void setAuthToken(AuthToken authToken) { this.authToken = authToken; } /** * Initialize the session with an authentication token and resource name. * * @param authToken the authentication token * @param resource the resource */ public void setAuthToken(AuthToken authToken, String resource) { setAddress(new JID(authToken.getUsername(), getServerName(), resource)); this.authToken = authToken; setStatus(Session.STATUS_AUTHENTICATED); // Add session to the session manager sessionManager.addSession(this); } /** * Indicates if the session has been initialized. * * @return true if the session has been initialized, false otherwise. */ public boolean isInitialized() { return initialized; } /** * Sets the initialization state of the session. * * @param initialized true if the session has been initialized */ public void setInitialized(boolean initialized) { this.initialized = initialized; } /** * Indicates if the session was available ever. * * @return true if the session was available ever, false otherwise. */ public boolean wasAvailable() { return wasAvailable; } /** * Returns the presence of this session. * * @return the presence */ public Presence getPresence() { return presence; } /** * Sets the presence of this session. * * @param presence the presence */ public void setPresence(Presence presence) { Presence oldPresence = this.presence; this.presence = presence; if (oldPresence.isAvailable() && !this.presence.isAvailable()) { setInitialized(false); } else if (!oldPresence.isAvailable() && this.presence.isAvailable()) { wasAvailable = true; } } /** * Returns a text with the available stream features. */ public String getAvailableStreamFeatures() { StringBuilder sb = new StringBuilder(); if (getAuthToken() == null) { // Supports Non-SASL Authentication sb.append("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>"); // Supports In-Band Registration sb .append("<register xmlns=\"http://jabber.org/features/iq-register\"/>"); } else { // If the session has been authenticated sb.append("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"); sb .append("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"); } return sb.toString(); } @Override public String toString() { return super.toString() + " presence: " + presence; } }