Commit 37bf168a authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gaston

Added support for external components. JM-5


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@945 b35dd754-fafc-0310-a699-88a17e54d16e
parent c5e9bc33
......@@ -12,29 +12,26 @@
package org.jivesoftware.messenger;
import org.jivesoftware.messenger.auth.AuthToken;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.xmpp.packet.Presence;
import org.xmpp.packet.JID;
import java.util.Date;
/**
* The session is the primary interface to the entire chat server.
* Use the session to obtain references to all system managers, permissions,
* authentication, and other resources.<p>
*
* The session represents a connection between the server and a client (c2s) or
* another server (s2s). Authentication and user accounts are associated with
* c2s connections while s2s has an optional authentication association but no
* single user user.<p>
* another server (s2s) as well as a connection with a component. Authentication and
* user accounts are associated with c2s connections while s2s has an optional authentication
* association but no single user user.<p>
*
* Obtain object managers from the session in order to access server resources.
*
* @author Iain Shigeoka
* @author Gaston Dombiak
*/
public abstract class Session implements RoutableChannelHandler {
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
*/
public interface Session extends RoutableChannelHandler {
protected static String CHARSET = "UTF-8";
public static final int STATUS_CLOSED = -1;
public static final int STATUS_CONNECTED = 1;
......@@ -42,109 +39,104 @@ public interface Session extends RoutableChannelHandler {
public static final int STATUS_AUTHENTICATED = 3;
/**
* Obtain the address of the user. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*
* @return the address of the packet handler.
* The Address this session is authenticated as.
*/
public JID getAddress();
private JID address;
/**
* Sets the new address of this session. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
* The stream id for this session (random and unique).
*/
public void setAddress(JID address);
private StreamID streamID;
/**
* Returns the connection associated with this Session.
*
* @return The connection for this session
* The current session status.
*/
public Connection getConnection();
protected int status = STATUS_CONNECTED;
/**
* Obtain the current status of this session.
*
* @return The status code for this session
* The connection that this session represents.
*/
public int getStatus();
protected Connection conn;
/**
* Set the new status of this session. Setting a status may trigger
* certain events to occur (setting a closed status will close this
* session).
*
* @param status The new status code for this session
* The authentication token for this session.
*/
public void setStatus(int status) throws UnauthorizedException;
protected AuthToken authToken;
/**
* Flag indicating if this session has been initialized once coming
* online. Session initialization occurs after the session receives
* the first "available" presence update from the client. Initialization
* actions include pushing offline messages, presence subscription requests,
* and presence statuses to the client. Initialization occurs only once
* following the first available presence transition.
*
* @return True if the session has already been initializsed
*/
public boolean isInitialized();
protected SessionManager sessionManager;
private String serverName;
private Date startDate = new Date();
private long lastActiveDate;
private long clientPacketCount = 0;
private long serverPacketCount = 0;
/**
* Sets the initialization state of the session.
* Creates a session with an underlying connection and permission protection.
*
* @param isInit True if the session has been initialized
* @throws UnauthorizedException If the caller does not have permission to make this change
* @see #isInitialized
* @param connection The connection we are proxying
*/
public void setInitialized(boolean isInit) throws UnauthorizedException;
public Session(String serverName, Connection connection, StreamID streamID) {
conn = connection;
this.streamID = streamID;
this.serverName = serverName;
String id = streamID.getID();
this.address = new JID(null, serverName, id);
this.sessionManager = SessionManager.getInstance();
}
/**
* Obtain the presence of this session.
* Obtain the address of the user. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*
* @return The presence of this session or null if not authenticated
* @return the address of the packet handler.
*/
public Presence getPresence();
public JID getAddress() {
return address;
}
/**
* Set the presence of this session
*
* @param presence The presence for the session
* @return The old priority of the session or null if not authenticated
* Sets the new address of this session. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*/
public Presence setPresence(Presence presence) throws UnauthorizedException;
public void setAddress(JID address){
this.address = address;
}
/**
* Initialize the session with a valid authentication token and
* resource name. This automatically upgrades the session's
* status to authenticated and enables many features that are not
* available until authenticated (obtaining managers for example).
* Returns the connection associated with this Session.
*
* @param auth The authentication token obtained from the AuthFactory
* @param resource The resource this session authenticated under
* @param userManager The user manager this authentication occured under
* @return The connection for this session
*/
public void setAuthToken(AuthToken auth, UserManager userManager, String resource)
throws UserNotFoundException, UnauthorizedException;
public Connection getConnection() {
return conn;
}
/**
* <p>Initialize the session as an anonymous login.</p>
* <p>This automatically upgrades the session's
* status to authenticated and enables many features that are not
* available until authenticated (obtaining managers for example).</p>
* Obtain the current status of this session.
*
* @return The status code for this session
*/
public void setAnonymousAuth() throws UnauthorizedException;
public int getStatus() {
return status;
}
/**
* <p>Obtain the authentication token associated with this session.</p>
* Set the new status of this session. Setting a status may trigger
* certain events to occur (setting a closed status will close this
* session).
*
* @return The authentication token associated with this session (can be null)
* @param status The new status code for this session
*/
public AuthToken getAuthToken();
public void setStatus(int status) {
this.status = status;
}
/**
* Obtain the stream ID associated with this sesison. Stream ID's are generated by the server
......@@ -152,85 +144,68 @@ public interface Session extends RoutableChannelHandler {
*
* @return This session's assigned stream ID
*/
public StreamID getStreamID();
/**
* Returns the username associated with this session. Use this information
* with the user manager to obtain the user based on username.
*
* @return the username associated with this session
* @throws UserNotFoundException if a user is not associated with a session
* (the session has not authenticated yet)
* @throws UnauthorizedException If caller doesn't have permission to access this information.
*/
public String getUsername() throws UserNotFoundException, UnauthorizedException;
public StreamID getStreamID() {
return streamID;
}
/**
* Obtain the name of the server this session belongs to.
*
* @return the server name.
*/
public String getServerName();
public String getServerName() {
return serverName;
}
/**
* Obtain the date the session was created.
*
* @return the session's creation date.
*/
public Date getCreationDate();
public Date getCreationDate() {
return startDate;
}
/**
* Obtain the time the session last had activity.
*
* @return The last time the session received activity.
*/
public Date getLastActiveDate();
public Date getLastActiveDate() {
return new Date(lastActiveDate);
}
/**
* Obtain the number of packets sent from the client to the server.
*
* @throws UnauthorizedException If caller doesn't have permission to access this information
*/
public void incrementClientPacketCount() throws UnauthorizedException;
public void incrementClientPacketCount() {
clientPacketCount++;
lastActiveDate = System.currentTimeMillis();
}
/**
* Obtain the number of packets sent from the server to the client.
*
* @throws UnauthorizedException If caller doesn't have permission to access this information
*/
public void incrementServerPacketCount() throws UnauthorizedException;
public void incrementServerPacketCount() {
serverPacketCount++;
lastActiveDate = System.currentTimeMillis();
}
/**
* Obtain the number of packets sent from the client to the server.
*
* @return The number of packets sent from the client to the server.
*/
public long getNumClientPackets();
public long getNumClientPackets() {
return clientPacketCount;
}
/**
* Obtain the number of packets sent from the server to the client.
*
* @return The number of packets sent from the server to the client.
*/
public long getNumServerPackets();
/**
* Returns the number of conflicts detected on this session.
* Conflicts typically occur when another session authenticates properly
* to the user account and requests to use a resource matching the one
* in use by this session. Administrators may configure the server to automatically
* kick off existing sessions when their conflict count exceeds some limit including
* 0 (old sessions are kicked off immediately to accommodate new sessions). Conflicts
* typically signify the existing (old) session is broken/hung.
*
* @return The number of conflicts detected for this session
*/
public int getConflictCount();
/**
* Increments the conflict by one.
*
* @throws UnauthorizedException If caller doesn't have permission to access this information
*/
public void incrementConflictCount() throws UnauthorizedException;
public long getNumServerPackets() {
return serverPacketCount;
}
}
\ No newline at end of file
......@@ -22,11 +22,12 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jivesoftware.messenger.audit.AuditStreamIDFactory;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.spi.BasicStreamIDFactory;
import org.jivesoftware.messenger.spi.SessionImpl;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.handler.PresenceUpdateHandler;
......@@ -44,7 +45,7 @@ import org.xmpp.packet.Presence;
*
* @author Derek DeMoro
*/
public class SessionManager extends BasicModule implements ConnectionCloseListener {
public class SessionManager extends BasicModule {
private int sessionCount = 0;
public static final int NEVER_KICK = -1;
......@@ -56,7 +57,42 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
private UserManager userManager;
private int conflictLimit;
private Map<String, Session> preAuthenticatedSessions = new ConcurrentHashMap<String, Session>();
private ClientSessionListener clientSessionListener = new ClientSessionListener();
private ComponentSessionListener componentSessionListener = new ComponentSessionListener();
/**
* Map that holds sessions that has been created but haven't been authenticated yet. The Map
* will hold client sessions.
*/
private Map<String, ClientSession> preAuthenticatedSessions = new ConcurrentHashMap<String, ClientSession>();
/**
* Map of priority ordered SessionMap objects with username (toLowerCase) as key. The sessions
* contained in this Map are client sessions. For each username a SessionMap is kept which
* tracks the session for each user resource.
*/
private Map<String, SessionMap> sessions = new ConcurrentHashMap<String, SessionMap>();
/**
* Map of anonymous server sessions. They need to be treated separately as they
* have no associated user, and don't follow the normal routing rules for
* priority based fall over. The sessions contained in this Map are client sessions.
*/
private Map<String, ClientSession> anonymousSessions = new ConcurrentHashMap<String, ClientSession>();
/**
* The sessions contained in this List are component sessions. For each connected component
* this Map will keep the component's session.
*/
private List<ComponentSession> componentsSessions = new CopyOnWriteArrayList<ComponentSession>();
/**
* <p>Session manager must maintain the routing table as sessions are added and
* removed.</p>
*/
private RoutingTable routingTable;
private StreamIDFactory streamIDFactory;
/**
* Returns the instance of <CODE>SessionManagerImpl</CODE> being used by the XMPPServer.
......@@ -92,31 +128,12 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
}
/**
* Map of priority ordered SessionMap objects with username (toLowerCase) as key.
* The map and its contents should NOT be persisted to disk.
*/
private Map<String, SessionMap> sessions = new ConcurrentHashMap<String, SessionMap>();
/**
* <p>Session manager must maintain the routing table as sessions are added and
* removed.</p>
*/
private RoutingTable routingTable;
/**
* Map of anonymous server sessions. They need to be treated separately as they
* have no associated user, and don't follow the normal routing rules for
* priority based fall over.
*/
private Map<String, Session> anonymousSessions = new ConcurrentHashMap<String, Session>();
/**
* Simple data structure to track sessions for a single user (tracked by resource
* and priority).
*/
private class SessionMap {
private Map<String,Session> resources = new HashMap<String,Session>();
private Map<String,ClientSession> resources = new HashMap<String,ClientSession>();
private LinkedList priorityList = new LinkedList();
/**
......@@ -124,7 +141,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
*
* @param session
*/
void addSession(Session session) {
void addSession(ClientSession session) {
String resource = session.getAddress().getResource();
resources.put(resource, session);
Presence presence = session.getPresence();
......@@ -143,7 +160,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
if (priorityList.size() > 0) {
Iterator iter = priorityList.iterator();
for (int i = 0; iter.hasNext(); i++) {
Session sess = resources.get(iter.next());
ClientSession sess = resources.get(iter.next());
if (sess.getPresence().getPriority() <= priority) {
priorityList.add(i, resource);
break;
......@@ -186,7 +203,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @param resource The resource describing the particular session
* @return The session for that resource or null if none found (use getDefaultSession() to obtain default)
*/
Session getSession(String resource) {
ClientSession getSession(String resource) {
return resources.get(resource);
}
......@@ -209,7 +226,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* considered.
* @return The default session for the user.
*/
Session getDefaultSession(boolean filterAvailable) {
ClientSession getDefaultSession(boolean filterAvailable) {
if (priorityList.isEmpty()) {
return null;
}
......@@ -219,7 +236,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
else {
for (int i=0; i < priorityList.size(); i++) {
Session s = resources.get(priorityList.get(i));
ClientSession s = resources.get(priorityList.get(i));
if (s.getPresence().isAvailable()) {
return s;
}
......@@ -269,9 +286,9 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
*
* @return a collection of all the sessions whose presence is available.
*/
public Collection<Session> getAvailableSessions() {
LinkedList<Session> list = new LinkedList<Session>();
for (Session session : resources.values()) {
public Collection<ClientSession> getAvailableSessions() {
LinkedList<ClientSession> list = new LinkedList<ClientSession>();
for (ClientSession session : resources.values()) {
if (session.getPresence().isAvailable()) {
list.add(session);
}
......@@ -280,58 +297,66 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
}
private StreamIDFactory streamIDFactory;
/**
* Creates a new <tt>Session</tt>.
* Creates a new <tt>ClientSession</tt>.
*
* @param conn the connection to create the session from.
* @return a newly created session.
* @throws UnauthorizedException
*/
public Session createSession(Connection conn) throws UnauthorizedException {
public Session createClientSession(Connection conn) throws UnauthorizedException {
if (serverName == null) {
throw new UnauthorizedException("Server not initialized");
}
StreamID id = streamIDFactory.createStreamID();
Session session = new SessionImpl(serverName, conn, id);
ClientSession session = new ClientSession(serverName, conn, id);
conn.init(session);
conn.registerCloseListener(this, session);
// Register to receive close notification on this session so we can
// remove its route from the sessions set and also send an unavailable presence if it wasn't
// sent before
conn.registerCloseListener(clientSessionListener, session);
// Add to pre-authenticated sessions.
preAuthenticatedSessions.put(session.getAddress().toString(), session);
return session;
}
public Session createComponentSession(Connection conn) throws UnauthorizedException {
if (serverName == null) {
throw new UnauthorizedException("Server not initialized");
}
StreamID id = streamIDFactory.createStreamID();
ComponentSession session = new ComponentSession(serverName, conn, id);
conn.init(session);
// Register to receive close notification on this session so we can
// remove the external component from the list of components
conn.registerCloseListener(componentSessionListener, session);
// Add to component session.
componentsSessions.add(session);
return session;
}
/**
* Add a new session to be managed.
*/
public boolean addSession(Session session) {
public boolean addSession(ClientSession session) {
boolean success = false;
String username = session.getAddress().getNode().toLowerCase();
SessionMap resources = null;
synchronized(username.intern()) {
try {
resources = sessions.get(username);
if (resources == null) {
resources = new SessionMap();
sessions.put(username, resources);
}
resources.addSession(session);
// Register to recieve close notification on this session so we can
// remove its route from the sessions set. We hand the session back
// to ourselves in the message.
session.getConnection().registerCloseListener(this, session);
// Remove the pre-Authenticated session but remember to use the temporary JID as the key
preAuthenticatedSessions.remove(new JID(null, session.getAddress().getDomain(),
session.getStreamID().toString()).toString());
success = true;
}
catch (UnauthorizedException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
return success;
}
......@@ -343,7 +368,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
*
* @param session the session that receieved an available presence.
*/
public void sessionAvailable(Session session) {
public void sessionAvailable(ClientSession session) {
if (anonymousSessions.containsValue(session)) {
// Anonymous session always have resources so we only need to add one route. That is
// the route to the anonymous session
......@@ -379,12 +404,12 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @param session the session that received the new presence and therefore will not receive
* the notification.
*/
private void broadcastPresenceToOtherResource(Session session) throws UserNotFoundException,
UnauthorizedException {
private void broadcastPresenceToOtherResource(ClientSession session)
throws UserNotFoundException, UnauthorizedException {
Presence presence = null;
SessionMap sessionMap = sessions.get(session.getUsername());
if (sessionMap != null) {
for (Session userSession : sessionMap.getAvailableSessions()) {
for (ClientSession userSession : sessionMap.getAvailableSessions()) {
if (userSession != session) {
// Send the presence of an existing session to the session that has just changed
// the presence
......@@ -410,7 +435,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
*
* @param session the session that receieved an unavailable presence.
*/
public void sessionUnavailable(Session session) {
public void sessionUnavailable(ClientSession session) {
if (session.getAddress() != null && routingTable != null &&
session.getAddress().toBareJID().trim().length() != 0) {
// Remove route to the removed session (anonymous or not)
......@@ -497,7 +522,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @return The XMPPAddress best suited to use for delivery to the recipient
*/
public Session getBestRoute(JID recipient) {
Session session = null;
ClientSession session = null;
String resource = recipient.getResource();
String username = recipient.getNode();
if (username == null || "".equals(username)) {
......@@ -576,16 +601,16 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @param from the sender of the packet.
* @return the <code>Session</code> associated with the JID.
*/
public Session getSession(JID from) {
public ClientSession getSession(JID from) {
if (from == null) {
return null;
}
Session session = null;
ClientSession session = null;
// Initially Check preAuthenticated Sessions
session = preAuthenticatedSessions.get(from.toString());
if(session != null){
return session;
return (ClientSession)session;
}
String resource = from.getResource();
......@@ -620,8 +645,8 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
public Collection<Session> getSessions(SessionResultFilter filter) {
List<Session> results = new ArrayList<Session>();
public Collection<ClientSession> getSessions(SessionResultFilter filter) {
List<ClientSession> results = new ArrayList<ClientSession>();
if (filter != null) {
// Grab all the possible matching sessions by user
if (filter.getUsername() == null) {
......@@ -646,8 +671,8 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
// Now we have a copy of the references so we can spend some time
// doing the rest of the filtering without locking out session access
// so let's iterate and filter each session one by one
List<Session> filteredResults = new ArrayList<Session>();
for (Session session : results) {
List<ClientSession> filteredResults = new ArrayList<ClientSession>();
for (ClientSession session : results) {
// Now filter on creation date if needed
if (createMin != null || createMax != null) {
if (!isBetweenDates(session.getCreationDate(), createMin, createMax)) {
......@@ -689,11 +714,11 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
// Now generate the final list. I believe it's faster to to build up a new
// list than it is to remove items from head and tail of the sorted tree
List<Session> finalResults = new ArrayList<Session>();
List<ClientSession> finalResults = new ArrayList<ClientSession>();
int startIndex = filter.getStartIndex();
Iterator<Session> sortedIter = filteredResults.iterator();
Iterator<ClientSession> sortedIter = filteredResults.iterator();
for (int i = 0; sortedIter.hasNext() && finalResults.size() < maxResults; i++) {
Session result = sortedIter.next();
ClientSession result = sortedIter.next();
if (i >= startIndex) {
finalResults.add(result);
}
......@@ -763,7 +788,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
private void copyUserSessions(List sessions) {
// Get a copy of the sessions from all users
for (String username : getSessionUsers()) {
Collection<Session> usrSessions = getSessions(username);
Collection<ClientSession> usrSessions = getSessions(username);
for (Session session : usrSessions) {
sessions.add(session);
}
......@@ -785,8 +810,8 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
return Arrays.asList(anonymousSessions.values().toArray()).iterator();
}
public Collection<Session> getSessions(String username) {
List<Session> sessionList = new ArrayList<Session>();
public Collection<ClientSession> getSessions(String username) {
List<ClientSession> sessionList = new ArrayList<ClientSession>();
if (username != null) {
copyUserSessions(username, sessionList);
}
......@@ -859,7 +884,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
*
* @param session the session.
*/
public void removeSession(Session session) {
public void removeSession(ClientSession session) {
// TODO: Requires better error checking to ensure the session count is maintained
// TODO: properly (removal actually does remove).
if (session == null) {
......@@ -901,17 +926,11 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
}
public void addAnonymousSession(Session session) {
try {
public void addAnonymousSession(ClientSession session) {
anonymousSessions.put(session.getAddress().getResource(), session);
session.getConnection().registerCloseListener(this, session);
// Remove the session from the pre-Authenticated sessions list
preAuthenticatedSessions.remove(session.getAddress().toString());
}
catch (UnauthorizedException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
public int getConflictKickLimit() {
return conflictLimit;
......@@ -922,6 +941,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
}
private class ClientSessionListener implements ConnectionCloseListener {
/**
* Handle a session that just closed.
*
......@@ -929,7 +949,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
*/
public void onConnectionClose(Object handback) {
try {
Session session = (Session)handback;
ClientSession session = (ClientSession)handback;
if (session.getPresence().isAvailable()) {
// Send an unavailable presence to the user's subscribers
// Note: This gives us a chance to send an unavailable presence to the
......@@ -947,6 +967,28 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
Log.error(LocaleUtils.getLocalizedString("admin.error.close"), e);
}
}
}
private class ComponentSessionListener implements ConnectionCloseListener {
/**
* Handle a session that just closed.
*
* @param handback The session that just closed
*/
public void onConnectionClose(Object handback) {
try {
ComponentSession session = (ComponentSession)handback;
// Unbind the domain for this external component
ComponentManager.getInstance().removeComponent(session.getAddress().getDomain());
// Remove the session
componentsSessions.remove(session);
}
catch (Exception e) {
// Can't do anything about this problem...
Log.error(LocaleUtils.getLocalizedString("admin.error.close"), e);
}
}
}
public void initialize(XMPPServer server) {
super.initialize(server);
......
......@@ -11,23 +11,24 @@
package org.jivesoftware.messenger.handler;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.AuthFactory;
import org.jivesoftware.messenger.auth.AuthToken;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import java.util.ArrayList;
import java.util.List;
/**
* Implements the TYPE_IQ jabber:iq:auth protocol (plain only). Clients
* use this protocol to authenticate with the server. A 'get' query
......@@ -79,7 +80,7 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException {
try {
Session session = sessionManager.getSession(packet.getFrom());
ClientSession session = sessionManager.getSession(packet.getFrom());
IQ response = null;
try {
Element iq = packet.getElement();
......@@ -146,14 +147,15 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
}
private IQ login(String username, Element iq, IQ packet, IQ response, String password,
Session session, String digest) throws UnauthorizedException, UserNotFoundException
ClientSession session, String digest) throws UnauthorizedException,
UserNotFoundException
{
JID jid = localServer.createJID(username, iq.elementTextTrim("resource"));
// If a session already exists with the requested JID, then check to see
// if we should kick it off or refuse the new connection
if (sessionManager.isActiveRoute(jid)) {
Session oldSession = null;
ClientSession oldSession = null;
try {
oldSession = sessionManager.getSession(jid);
oldSession.incrementConflictCount();
......@@ -218,7 +220,7 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
return response;
}
private IQ anonymousLogin(Session session, IQ packet) throws UnauthorizedException {
private IQ anonymousLogin(ClientSession session, IQ packet) {
IQ response = IQ.createResultIQ(packet);;
if (anonymousAllowed) {
session.setAnonymousAuth();
......
......@@ -11,30 +11,32 @@
package org.jivesoftware.messenger.handler;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.disco.ServerFeaturesProvider;
import org.jivesoftware.messenger.forms.DataForm;
import org.jivesoftware.messenger.forms.FormField;
import org.jivesoftware.messenger.forms.spi.XDataFormImpl;
import org.jivesoftware.messenger.forms.spi.XFormFieldImpl;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.group.GroupManager;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.*;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserAlreadyExistsException;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.util.Log;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
/**
* Implements the TYPE_IQ jabber:iq:register protocol (plain only). Clients
* use this protocol to register a user account with the server.
......@@ -146,7 +148,7 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
return delegate.handleIQ(packet);
}
Session session = sessionManager.getSession(packet.getFrom());
ClientSession session = sessionManager.getSession(packet.getFrom());
IQ reply = null;
// If inband registration is not allowed, return an error.
if (!enabled) {
......@@ -185,9 +187,6 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
catch (UserNotFoundException e) {
reply.setChildElement(probeResult.createCopy());
}
catch (UnauthorizedException e) {
reply.setChildElement(probeResult.createCopy());
}
}
else {
// This is a workaround. Since we don't want to have an incorrect TO attribute
......
......@@ -11,15 +11,21 @@
package org.jivesoftware.messenger.handler;
import org.jivesoftware.messenger.disco.ServerFeaturesProvider;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.roster.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.*;
import org.jivesoftware.messenger.disco.ServerFeaturesProvider;
import org.jivesoftware.messenger.roster.Roster;
import org.xmpp.packet.*;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserAlreadyExistsException;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import java.util.ArrayList;
import java.util.Iterator;
......@@ -157,7 +163,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
UserAlreadyExistsException, SharedGroupException {
IQ returnPacket = null;
Session session = sessionManager.getSession(packet.getFrom());
ClientSession session = sessionManager.getSession(packet.getFrom());
IQ.Type type = packet.getType();
......
......@@ -11,18 +11,9 @@
package org.jivesoftware.messenger.net;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.Socket;
import java.net.SocketException;
import org.dom4j.Element;
import org.dom4j.io.XPPPacketReader;
import org.jivesoftware.messenger.Connection;
import org.jivesoftware.messenger.PacketRouter;
import org.jivesoftware.messenger.Session;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.audit.Auditor;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.util.LocaleUtils;
......@@ -35,6 +26,13 @@ import org.xmpp.packet.Message;
import org.xmpp.packet.Presence;
import org.xmpp.packet.Roster;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.Socket;
import java.net.SocketException;
/**
* Reads XMPP XML from a socket.
*
......@@ -47,9 +45,6 @@ public class SocketReadThread extends Thread {
*/
private static String CHARSET = "UTF-8";
private static final String ETHERX_NAMESPACE = "http://etherx.jabber.org/streams";
private static final String FLASH_NAMESPACE = "http://www.jabber.com/streams/flash";
private Socket sock;
private Session session;
private Connection connection;
......@@ -73,16 +68,15 @@ public class SocketReadThread extends Thread {
* @param serverName The name of the server this socket is working for
* @param auditor The audit manager that will audit incoming packets
* @param sock The socket to read from
* @param session The session being read
* @param conn The connection being read
*/
public SocketReadThread(PacketRouter router, String serverName, Auditor auditor, Socket sock,
Session session) {
Connection conn) {
super("SRT reader");
this.serverName = serverName;
this.router = router;
this.auditor = auditor;
this.session = session;
connection = session.getConnection();
this.connection = conn;
this.sock = sock;
}
......@@ -122,7 +116,8 @@ public class SocketReadThread extends Thread {
// unavailable presence
if (clearSignout == false) {
if (session != null && session.getStatus() == Session.STATUS_AUTHENTICATED) {
Presence presence = session.getPresence();
if (session instanceof ClientSession) {
Presence presence = ((ClientSession) session).getPresence();
if (presence != null) {
// Simulate an unavailable presence sent by the user.
Presence packet = presence.createCopy();
......@@ -133,6 +128,7 @@ public class SocketReadThread extends Thread {
}
}
}
}
// It is normal for clients to abruptly cut a connection
// rather than closing the stream document
// Since this is normal behavior, we won't log it as an error
......@@ -224,88 +220,44 @@ public class SocketReadThread extends Thread {
}
/**
* Uses the XPP to grab the opening stream tag and create
* an active session object. In all cases, the method obtains the
* opening stream tag, checks for errors, and either creates a session
* or returns an error and kills the connection. If the connection
* remains open, the XPP will be set to be ready for the first packet.
* A call to next() should result in an START_TAG state with the first
* packet in the stream.
*
* @throws UnauthorizedException If the caller did not have permission
* to use this method.
* Uses the XPP to grab the opening stream tag and create an active session
* object. The session to create will depend on the sent namespace. In all
* cases, the method obtains the opening stream tag, checks for errors, and
* either creates a session or returns an error and kills the connection.
* If the connection remains open, the XPP will be set to be ready for the
* first packet. A call to next() should result in an START_TAG state with
* the first packet in the stream.
*/
private void createSession() throws UnauthorizedException, XmlPullParserException, IOException, Exception {
private void createSession() throws UnauthorizedException, XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser();
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
boolean isFlashClient = xpp.getPrefix().equals("flash");
// Conduct error checking, the opening tag should be 'stream'
// in the 'etherx' namespace
if (!xpp.getName().equals("stream") && !isFlashClient) {
throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-stream"));
// Create the correct session based on the sent namespace
if ("jabber:client".equals(xpp.getNamespaceUri(xpp.getDepth()-1))) {
// The connected client is a regular client so create a ClientSession
session = ClientSession.createSession(serverName, reader, connection);
}
if (!xpp.getNamespace(xpp.getPrefix()).equals(ETHERX_NAMESPACE) &&
!(isFlashClient && xpp.getNamespace(xpp.getPrefix()).equals(FLASH_NAMESPACE))) {
throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-namespace"));
}
// TODO Should we keep the language requested by the client in the session so that future
// messages to the client may use the correct resource bundle? So far we are only answering
// the same language specified by the client (if any) or if none then answer a default
// language
String language = "en";
for (int i = 0; i < xpp.getAttributeCount(); i++) {
if ("lang".equals(xpp.getAttributeName(i))) {
language = xpp.getAttributeValue(i);
else if ("jabber:component:accept".equals(xpp.getNamespaceUri(xpp.getDepth()-1))) {
// The connected client is a component so create a ComponentSession
session = ComponentSession.createSession(serverName, reader, connection);
}
}
else {
Writer writer = connection.getWriter();
// Build the start packet response
StringBuffer sb = new StringBuffer();
sb.append("<?xml version='1.0' encoding='");
sb.append(CHARSET);
sb.append("'?>");
if (isFlashClient) {
sb.append("<flash:stream xmlns:flash=\"http://www.jabber.com/streams/flash\" ");
}
else {
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("\">");
// Include the bad-namespace-prefix in the response
sb.append("<stream:error>");
sb.append("<bad-namespace-prefix xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\"/>");
sb.append("</stream:error>");
sb.append("</stream:stream>");
writer.write(sb.toString());
// If this is a flash client then flag the connection and append a special caracter to the
// response
if (isFlashClient) {
session.getConnection().setFlashClient(true);
writer.write('\0');
// Skip possible end of tags and \0 characters
/*final int[] holderForStartAndLength = new int[2];
final char[] chars = xpp.getTextCharacters(holderForStartAndLength);
if (chars[xpp.getColumnNumber()-2] == '/') {
xpp.next();
try {
xpp.next();
}
catch (XmlPullParserException ie) {
// We expect this exception since the parser is reading a \0 character
}
}*/
}
writer.flush();
// TODO: check for SASL support in opening stream tag
// Close the underlying connection
connection.close();
}
}
}
......@@ -11,16 +11,16 @@
package org.jivesoftware.messenger.spi;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.audit.AuditManager;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.net.SSLSocketAcceptThread;
import org.jivesoftware.messenger.net.SocketAcceptThread;
import org.jivesoftware.messenger.net.SocketConnection;
import org.jivesoftware.messenger.net.SocketReadThread;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
......@@ -113,16 +113,11 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
auditManager.getAuditor(),
sock,
isSecure);
Session session = sessionManager.createSession(conn);
SocketReadThread reader = new SocketReadThread(router,
serverName, auditManager.getAuditor(),
sock, session);
SocketReadThread reader = new SocketReadThread(router, serverName,
auditManager.getAuditor(), sock, conn);
reader.setDaemon(true);
reader.start();
}
catch (UnauthorizedException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
catch (IOException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
......
......@@ -11,21 +11,7 @@
package org.jivesoftware.messenger.spi;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jivesoftware.messenger.Component;
import org.jivesoftware.messenger.ComponentManager;
import org.jivesoftware.messenger.PacketDeliverer;
import org.jivesoftware.messenger.PresenceManager;
import org.jivesoftware.messenger.Session;
import org.jivesoftware.messenger.SessionManager;
import org.jivesoftware.messenger.XMPPServer;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager;
......@@ -37,6 +23,9 @@ import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
import org.xmpp.packet.Presence;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Simple in memory implementation of the PresenceManager interface.
*
......@@ -238,7 +227,7 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
}
Presence presence = null;
for (Session session : sessionManager.getSessions(user.getUsername())) {
for (ClientSession session : sessionManager.getSessions(user.getUsername())) {
if (presence == null) {
presence = session.getPresence();
}
......@@ -263,7 +252,7 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
}
List<Presence> presences = new ArrayList<Presence>();
for (Session session : sessionManager.getSessions(user.getUsername())) {
for (ClientSession session : sessionManager.getSessions(user.getUsername())) {
presences.add(session.getPresence());
}
return Collections.unmodifiableCollection(presences);
......@@ -298,9 +287,9 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
Component component = getPresenceComponent(probee);
if (server.isLocal(probee)) {
if (probee.getNode() != null && !"".equals(probee.getNode())) {
Collection<Session> sessions =
Collection<ClientSession> sessions =
sessionManager.getSessions(probee.getNode());
for (Session session : sessions) {
for (ClientSession session : sessions) {
Presence presencePacket = session.getPresence().createCopy();
presencePacket.setFrom(session.getAddress());
try {
......@@ -317,7 +306,7 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
presence.setType(Presence.Type.probe);
presence.setFrom(server.createJID(prober, ""));
presence.setTo(probee);
component.processPacket(presence);
component.process(presence);
}
else {
Presence presence = (Presence) foreignUserCache.get(probee.toBareJID());
......@@ -345,9 +334,9 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
public void probePresence(JID prober, JID probee) {
try {
if (server.isLocal(probee)) {
Collection<Session> sessions =
Collection<ClientSession> sessions =
sessionManager.getSessions(probee.getNode());
for (Session session : sessions) {
for (ClientSession session : sessions) {
Presence presencePacket = session.getPresence().createCopy();
presencePacket.setFrom(session.getAddress());
try {
......
......@@ -38,7 +38,7 @@
// Get the session & address objects
SessionManager sessionManager = webManager.getSessionManager();
JID address = new JID(jid);
Session currentSess = sessionManager.getSession(address);
ClientSession currentSess = sessionManager.getSession(address);
boolean isAnonymous = address.getNode() == null || "".equals(address.getNode());
// Get a presence manager
......@@ -57,7 +57,7 @@
}
// See if there are multiple sessions for this user:
Collection<Session> sessions = null;
Collection<ClientSession> sessions = null;
int sessionCount = sessionManager.getSessionCount(address.getNode());
if (!isAnonymous && sessionCount > 1) {
sessions = sessionManager.getSessions(address.getNode());
......@@ -279,7 +279,7 @@ user <b><%= address.getNode() %></b> has multiple sessions open, they will appea
<% int count = 0;
String linkURL = "session-details.jsp";
for (Session sess : sessions) {
for (ClientSession sess : sessions) {
count++;
boolean current = sess.getAddress().equals(address);
%>
......
......@@ -130,7 +130,7 @@ Below is a list of sessions on this server.
SessionResultFilter filter = new SessionResultFilter();
filter.setStartIndex(start);
filter.setNumResults(range);
Collection<Session> sessions = sessionManager.getSessions(filter);
Collection<ClientSession> sessions = sessionManager.getSessions(filter);
if (sessions.isEmpty()) {
%>
<tr>
......@@ -146,7 +146,7 @@ Below is a list of sessions on this server.
<% int count = start;
boolean current = false; // needed in session-row.jspf
String linkURL = "session-details.jsp";
for (Session sess : sessions) {
for (ClientSession sess : sessions) {
count++;
%>
<%@ include file="session-row.jspf" %>
......
......@@ -105,13 +105,13 @@
// Get all sessions associated with this user:
int numSessions = -1;
Session sess = null;
Collection<Session> sessions = null;
ClientSession sess = null;
Collection<ClientSession> sessions = null;
if (user != null) {
numSessions = sessionManager.getSessionCount(user.getUsername());
sessions = sessionManager.getSessions(user.getUsername());
if (numSessions == 1) {
sess = (Session)sessions.iterator().next();
sess = sessions.iterator().next();
}
}
%>
......@@ -205,9 +205,9 @@ function updateSelect(el) {
<select size="2" name="jid" multiple>
<% Iterator iter = sessions.iterator();
<% Iterator<ClientSession> iter = sessions.iterator();
while (iter.hasNext()) {
sess = (Session)iter.next();
sess = iter.next();
%>
<option value="<%= sess.getAddress().toString() %>"><%= sess.getAddress().toString() %></option>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment