Commit ae0aba65 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gaston

Fixed so that only sessions whose presences are available are eligible for...

Fixed so that only sessions whose presences are available are eligible for receiving messages. JM-116


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@853 b35dd754-fafc-0310-a699-88a17e54d16e
parent 8f8909e0
......@@ -200,20 +200,32 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
/**
* Returns the default session for the user based on presence
* priority.
* Returns the default session for the user based on presence priority. It's possible to
* indicate if only available sessions (i.e. with an available presence) should be
* included in the search.
*
* @param filterAvailable flag that indicates if only available sessions should be
* considered.
* @return The default session for the user.
*/
Session getDefaultSession() {
Session getDefaultSession(boolean filterAvailable) {
if (priorityList.isEmpty()) {
return null;
}
// return (Session) resources.get(priorityList.getFirst());
Session s = resources.get(priorityList.getFirst());
if (!filterAvailable) {
return resources.get(priorityList.getFirst());
}
else {
for (int i=0; i < priorityList.size(); i++) {
Session s = resources.get(priorityList.get(i));
if (s.getPresence().isAvailable()) {
return s;
}
}
return null;
}
}
/**
* Determines if this map is empty or not.
......@@ -230,6 +242,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @param packet
*/
private void broadcast(Packet packet) throws UnauthorizedException, PacketException {
// TODO Should we filter existing sessions whose presence is not available?
for (Session session : resources.values()) {
packet.setTo(session.getAddress());
session.getConnection().deliver(packet);
......@@ -250,6 +263,21 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
}
return list.iterator();
}
/**
* Returns a collection of all the sessions whose presence is available.
*
* @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()) {
if (session.getPresence().isAvailable()) {
list.add(session);
}
}
return list;
}
}
private StreamIDFactory streamIDFactory;
......@@ -304,19 +332,83 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
if (success) {
Session defaultSession = resources.getDefaultSession();
JID node = new JID(defaultSession.getAddress().getNode(), defaultSession.getAddress().getDomain(), null);
return success;
}
/**
* Notification message sent when a client sent an available presence for the session. Making
* the session available means that the session is now eligible for receiving messages from
* other clients. Sessions whose presence is not available may only receive packets (IQ packets)
* from the server. Therefore, an unavailable session remains invisible to other clients.
*
* @param session the session that receieved an available presence.
*/
public void sessionAvailable(Session 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
routingTable.addRoute(session.getAddress(), session);
}
else {
// A non-anonymous session is now available
Session defaultSession = null;
try {
defaultSession = sessions.get(session.getUsername()).getDefaultSession(true);
JID node = new JID(defaultSession.getAddress().getNode(),
defaultSession.getAddress().getDomain(), null);
// Add route to default session (used when no resource is specified)
routingTable.addRoute(node, defaultSession);
// Add route to the new session
routingTable.addRoute(session.getAddress(), session);
}
return success;
catch (UserNotFoundException e) {
// Do nothing since the session is anonymous (? - shouldn't happen)
}
catch (UnauthorizedException e) {
// Do nothing
}
}
}
/**
* Change the priority of a session associated with the sender.
* Notification message sent when a client sent an unavailable presence for the session. Making
* the session unavailable means that the session is not eligible for receiving messages from
* other clients.
*
* @param session the session that receieved an unavailable presence.
*/
public void sessionUnavailable(Session session) {
if (session.getAddress() != null && routingTable != null &&
session.getAddress().toBareJID().trim().length() != 0) {
// Remove route to the removed session (anonymous or not)
routingTable.removeRoute(session.getAddress());
try {
SessionMap sessionMap = sessions.get(session.getUsername());
// If all the user sessions are gone then remove the route to the default session
if (sessionMap.getAvailableSessions().isEmpty()) {
// Remove the route for the session's BARE address
routingTable.removeRoute(new JID(session.getAddress().getNode(),
session.getAddress().getDomain(), ""));
}
else {
// Update the route for the session's BARE address
Session defaultSession = sessionMap.getDefaultSession(true);
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(),
defaultSession.getAddress().getDomain(), ""),
defaultSession);
}
}
catch (UserNotFoundException e) {
// Do nothing since the session is anonymous
}
catch (UnauthorizedException e) {
// Do nothing
}
}
}
/**
* Change the priority of a session, that was already available, associated with the sender.
*
* @param sender The sender who's session just changed priority
* @param priority The new priority for the session
......@@ -331,23 +423,22 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
resources.changePriority(sender, priority);
// Get the session with highest priority
Session defaultSession = resources.getDefaultSession();
Session defaultSession = resources.getDefaultSession(true);
// Update the route to the bareJID with the session with highest priority
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(), defaultSession.getAddress().getDomain(), ""),
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(),
defaultSession.getAddress().getDomain(), ""),
defaultSession);
}
}
/**
* Retrieve the best route to deliver packets to this session
* given the recipient jid. If no active routes exist, this method
* returns a reference to itself (the account can store the packet).
* A null recipient chooses the default active route for this account
* if one exists. If the recipient can't be reached by this account
* (wrong account) an exception is thrown.
* Retrieve the best route to deliver packets to this session given the recipient jid. If the
* requested JID does not have a node (i.e. username) then the best route will be looked up
* in the anonymous sessions list. Otherwise, try to find a root for the exact JID
* (i.e. including the resource) and if none is found then answer the deafult session if any.
*
* @param recipient The recipient ID to send to or null to select the default route
* @param recipient The recipient ID to deliver packets to
* @return The XMPPAddress best suited to use for delivery to the recipient
*/
public Session getBestRoute(JID recipient) {
......@@ -368,12 +459,12 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
SessionMap sessionMap = sessions.get(username);
if (sessionMap != null) {
if (resource == null) {
session = sessionMap.getDefaultSession();
session = sessionMap.getDefaultSession(false);
}
else {
session = sessionMap.getSession(resource);
if (session == null) {
session = sessionMap.getDefaultSession();
session = sessionMap.getDefaultSession(false);
}
}
}
......@@ -680,6 +771,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @param packet The packet to be broadcast
*/
public void broadcast(Packet packet) throws UnauthorizedException {
// TODO Should we filter existing sessions whose presence is not available?
Iterator values = sessions.values().iterator();
while (values.hasNext()) {
((SessionMap)values.next()).broadcast(packet);
......@@ -698,6 +790,7 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
* @param packet The packet to be broadcast
*/
public void userBroadcast(String username, Packet packet) throws UnauthorizedException, PacketException {
// TODO Should we filter existing sessions whose presence is not available?
SessionMap sessionMap = sessions.get(username);
if (sessionMap != null) {
sessionMap.broadcast(packet);
......@@ -743,23 +836,6 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
offline.setType(Presence.Type.unavailable);
router.route(offline);
}
if (session.getAddress() != null && routingTable != null && session.getAddress().toBareJID().trim().length() != 0) {
// Remove route to the removed session
routingTable.removeRoute(session.getAddress());
if (sessionMap != null) {
// If all the user sessions are gone then remove the route to the default session
if (sessionMap.isEmpty()) {
// Remove the route for the session's BARE address
routingTable.removeRoute(new JID(session.getAddress().getNode(), session.getAddress().getDomain(), ""));
}
else {
// Update the route for the session's BARE address
Session defaultSession = sessionMap.getDefaultSession();
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(), defaultSession.getAddress().getDomain(), ""),
defaultSession);
}
}
}
}
public void addAnonymousSession(Session session) {
......@@ -768,9 +844,6 @@ public class SessionManager extends BasicModule implements ConnectionCloseListen
session.getConnection().registerCloseListener(this, session);
// Remove the session from the pre-Authenticated sessions list
preAuthenticatedSessions.remove(session.getAddress().toString());
// Anonymous session always have resources so we only need to add one route. That is
// the route to the anonymous session
routingTable.addRoute(session.getAddress(), session);
}
catch (UnauthorizedException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
......
......@@ -93,13 +93,9 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
// This is a workaround. Since we don't want to have an incorrect TO attribute
// value we need to clean up the TO attribute and send directly the response.
// The TO attribute will contain an incorrect value since we are setting a fake
// JID until the user actually authenticates with the server. We need to send
// the response directly since SocketPacketWriterHandler requires a TO or FROM
// attribute which this response doesn't have.
// JID until the user actually authenticates with the server.
if (session.getStatus() != Session.STATUS_AUTHENTICATED) {
response.setTo((JID)null);
session.getConnection().deliver(response);
return null;
}
}
// Otherwise set query
......@@ -138,7 +134,10 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
response = IQ.createResultIQ(packet);
response.setError(PacketError.Condition.not_authorized);
}
deliverer.deliver(response);
// Send the response directly since we want to be sure that we are sending it back
// to the correct session. Any other session of the same user but with different
// resource is incorrect.
session.getConnection().deliver(response);
}
catch (Exception e) {
Log.error("Error handling authentication IQ packet", e);
......
......@@ -81,7 +81,9 @@ public class SessionImpl implements Session {
this.serverName = serverName;
String id = streamID.getID();
this.address = new JID(null, serverName, id);
// Set an unavailable initial presence
presence = new Presence();
presence.setType(Presence.Type.unavailable);
this.sessionManager = SessionManager.getInstance();
......@@ -158,7 +160,20 @@ public class SessionImpl implements Session {
public Presence setPresence(Presence presence) {
Presence oldPresence = this.presence;
this.presence = presence;
if (oldPresence.getPriority() != this.presence.getPriority()) {
if (oldPresence.isAvailable() && !this.presence.isAvailable()) {
// The client is no longer available
sessionManager.sessionUnavailable(this);
// Mark that the session is no longer initialized. This means that if the user sends
// an available presence again the session will be initialized again thus receiving
// offline messages and offline presence subscription requests
setInitialized(false);
}
else if (!oldPresence.isAvailable() && this.presence.isAvailable()) {
// The client is available
sessionManager.sessionAvailable(this);
}
else if (oldPresence.getPriority() != this.presence.getPriority()) {
// The client has changed the priority of his presence
sessionManager.changePriority(getAddress(), this.presence.getPriority());
}
return oldPresence;
......
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