Commit cc51a80e authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Fixed several shared roster issues. JM-775 JM-776 JM-777

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@4442 b35dd754-fafc-0310-a699-88a17e54d16e
parent 3497cca2
...@@ -52,9 +52,9 @@ public class Roster implements Cacheable { ...@@ -52,9 +52,9 @@ public class Roster implements Cacheable {
protected ConcurrentHashMap<String, RosterItem> rosterItems = new ConcurrentHashMap<String, RosterItem>(); protected ConcurrentHashMap<String, RosterItem> rosterItems = new ConcurrentHashMap<String, RosterItem>();
/** /**
* Contacts with subscription FROM that only exist due to shared groups * Contacts with subscription FROM that only exist due to shared groups
* key: jabberid string; value: replciated key value. * key: jabberid string; value: groups why the implicit roster item exists (aka invisibleSharedGroups).
*/ */
protected ConcurrentHashMap<String, String> implicitFrom = new ConcurrentHashMap<String, String>(); protected ConcurrentHashMap<String, Set<String>> implicitFrom = new ConcurrentHashMap<String, Set<String>>();
private RosterItemProvider rosterItemProvider; private RosterItemProvider rosterItemProvider;
private String username; private String username;
...@@ -153,7 +153,8 @@ public class Roster implements Cacheable { ...@@ -153,7 +153,8 @@ public class Roster implements Cacheable {
} }
else { else {
// Cache information about shared contacts with subscription status FROM // Cache information about shared contacts with subscription status FROM
implicitFrom.put(item.getJid().toBareJID(), item.getJid().toBareJID()); implicitFrom
.put(item.getJid().toBareJID(), item.getInvisibleSharedGroupsNames());
} }
} }
catch (UserNotFoundException e) { catch (UserNotFoundException e) {
...@@ -220,9 +221,12 @@ public class Roster implements Cacheable { ...@@ -220,9 +221,12 @@ public class Roster implements Cacheable {
* user and the susbcription only exists due to some shared groups or otherwise null. * user and the susbcription only exists due to some shared groups or otherwise null.
*/ */
private RosterItem getImplicitRosterItem(JID user) { private RosterItem getImplicitRosterItem(JID user) {
if (implicitFrom.containsKey(user.toBareJID())) { Set<String> invisibleSharedGroups = implicitFrom.get(user.toBareJID());
return new RosterItem(user, RosterItem.SUB_FROM, RosterItem.ASK_NONE, if (invisibleSharedGroups != null) {
RosterItem rosterItem = new RosterItem(user, RosterItem.SUB_FROM, RosterItem.ASK_NONE,
RosterItem.RECV_NONE, "", null); RosterItem.RECV_NONE, "", null);
rosterItem.setInvisibleSharedGroupsNames(invisibleSharedGroups);
return rosterItem;
} }
return null; return null;
} }
...@@ -325,6 +329,11 @@ public class Roster implements Cacheable { ...@@ -325,6 +329,11 @@ public class Roster implements Cacheable {
* @throws UserNotFoundException If the roster item for the given user doesn't already exist * @throws UserNotFoundException If the roster item for the given user doesn't already exist
*/ */
public void updateRosterItem(RosterItem item) throws UserNotFoundException { public void updateRosterItem(RosterItem item) throws UserNotFoundException {
// Check if we need to convert an implicit roster item into an explicit one
if (implicitFrom.remove(item.getJid().toBareJID()) != null) {
// Ensure that the item is an explicit roster item
rosterItems.put(item.getJid().toBareJID(), item);
}
if (rosterItems.putIfAbsent(item.getJid().toBareJID(), item) == null) { if (rosterItems.putIfAbsent(item.getJid().toBareJID(), item) == null) {
rosterItems.remove(item.getJid().toBareJID()); rosterItems.remove(item.getJid().toBareJID());
if (item.getSubStatus() != RosterItem.SUB_NONE) { if (item.getSubStatus() != RosterItem.SUB_NONE) {
...@@ -381,7 +390,7 @@ public class Roster implements Cacheable { ...@@ -381,7 +390,7 @@ public class Roster implements Cacheable {
public RosterItem deleteRosterItem(JID user, boolean doChecking) throws SharedGroupException { public RosterItem deleteRosterItem(JID user, boolean doChecking) throws SharedGroupException {
// Answer an error if user (i.e. contact) to delete belongs to a shared group // Answer an error if user (i.e. contact) to delete belongs to a shared group
RosterItem itemToRemove = rosterItems.get(user.toBareJID()); RosterItem itemToRemove = rosterItems.get(user.toBareJID());
if (doChecking && itemToRemove != null && itemToRemove.isShared()) { if (doChecking && itemToRemove != null && !itemToRemove.getSharedGroups().isEmpty()) {
throw new SharedGroupException("Cannot remove contact that belongs to a shared group"); throw new SharedGroupException("Cannot remove contact that belongs to a shared group");
} }
...@@ -426,6 +435,20 @@ public class Roster implements Cacheable { ...@@ -426,6 +435,20 @@ public class Roster implements Cacheable {
return item; return item;
} }
else {
// Verify if the item being removed is an implicit roster item
// that only exists due to some shared group
if (implicitFrom.remove(user.toBareJID()) != null) {
// If the contact being removed is not a local user then ACK unsubscription
if (!server.isLocal(user)) {
Presence presence = new Presence();
presence.setFrom(server.createJID(username, null));
presence.setTo(user);
presence.setType(Presence.Type.unsubscribed);
server.getPacketRouter().route(presence);
}
}
}
return null; return null;
} }
...@@ -752,11 +775,13 @@ public class Roster implements Cacheable { ...@@ -752,11 +775,13 @@ public class Roster implements Cacheable {
// Remove from memory and do nothing else // Remove from memory and do nothing else
rosterItems.remove(item.getJid().toBareJID()); rosterItems.remove(item.getJid().toBareJID());
// Cache information about shared contacts with subscription status FROM // Cache information about shared contacts with subscription status FROM
implicitFrom.put(item.getJid().toBareJID(), item.getJid().toBareJID()); implicitFrom.put(item.getJid().toBareJID(), item.getInvisibleSharedGroupsNames());
} }
else { else {
// Remove from list of shared contacts with status FROM (if any) // Remove from list of shared contacts with status FROM (if any)
implicitFrom.remove(item.getJid().toBareJID()); implicitFrom.remove(item.getJid().toBareJID());
// Ensure that the item is an explicit roster item
rosterItems.put(item.getJid().toBareJID(), item);
// Brodcast to all the user resources of the updated roster item // Brodcast to all the user resources of the updated roster item
broadcast(item, true); broadcast(item, true);
// Probe the presence of the new group user // Probe the presence of the new group user
...@@ -861,11 +886,13 @@ public class Roster implements Cacheable { ...@@ -861,11 +886,13 @@ public class Roster implements Cacheable {
// Remove from memory and do nothing else // Remove from memory and do nothing else
rosterItems.remove(item.getJid().toBareJID()); rosterItems.remove(item.getJid().toBareJID());
// Cache information about shared contacts with subscription status FROM // Cache information about shared contacts with subscription status FROM
implicitFrom.put(item.getJid().toBareJID(), item.getJid().toBareJID()); implicitFrom.put(item.getJid().toBareJID(), item.getInvisibleSharedGroupsNames());
} }
else { else {
// Remove from list of shared contacts with status FROM (if any) // Remove from list of shared contacts with status FROM (if any)
implicitFrom.remove(item.getJid().toBareJID()); implicitFrom.remove(item.getJid().toBareJID());
// Ensure that the item is an explicit roster item
rosterItems.put(item.getJid().toBareJID(), item);
// Brodcast to all the user resources of the updated roster item // Brodcast to all the user resources of the updated roster item
broadcast(item, true); broadcast(item, true);
// Probe the presence of the new group user // Probe the presence of the new group user
...@@ -893,7 +920,8 @@ public class Roster implements Cacheable { ...@@ -893,7 +920,8 @@ public class Roster implements Cacheable {
int groupSize = item.getSharedGroups().size() + item.getInvisibleSharedGroups().size(); int groupSize = item.getSharedGroups().size() + item.getInvisibleSharedGroups().size();
if (item.isOnlyShared() && groupSize == 1) { if (item.isOnlyShared() && groupSize == 1) {
// Do nothing if the existing shared group is not the sharedGroup to remove // Do nothing if the existing shared group is not the sharedGroup to remove
if (!item.getSharedGroups().contains(sharedGroup)) { if (!item.getSharedGroups().contains(sharedGroup) &&
!item.getInvisibleSharedGroups().contains(sharedGroup)) {
return; return;
} }
// Delete the roster item from the roster since it exists only because of this // Delete the roster item from the roster since it exists only because of this
......
...@@ -349,7 +349,9 @@ public class RosterItem implements Cacheable { ...@@ -349,7 +349,9 @@ public class RosterItem implements Cacheable {
try { try {
it.remove(); it.remove();
} }
catch (IllegalStateException e) {} catch (IllegalStateException e) {
// Do nothing
}
} }
} }
catch (GroupNotFoundException e) { catch (GroupNotFoundException e) {
...@@ -362,7 +364,9 @@ public class RosterItem implements Cacheable { ...@@ -362,7 +364,9 @@ public class RosterItem implements Cacheable {
try { try {
it.remove(); it.remove();
} }
catch (IllegalStateException ise) {} catch (IllegalStateException ise) {
// Do nothing
}
} }
} }
} }
...@@ -383,6 +387,7 @@ public class RosterItem implements Cacheable { ...@@ -383,6 +387,7 @@ public class RosterItem implements Cacheable {
groups.add(GroupManager.getInstance().getGroup(groupName)); groups.add(GroupManager.getInstance().getGroup(groupName));
} }
catch (GroupNotFoundException e) { catch (GroupNotFoundException e) {
// Do nothing
} }
} }
return groups; return groups;
...@@ -402,11 +407,20 @@ public class RosterItem implements Cacheable { ...@@ -402,11 +407,20 @@ public class RosterItem implements Cacheable {
groups.add(GroupManager.getInstance().getGroup(groupName)); groups.add(GroupManager.getInstance().getGroup(groupName));
} }
catch (GroupNotFoundException e) { catch (GroupNotFoundException e) {
// Do nothing
} }
} }
return groups; return groups;
} }
Set<String> getInvisibleSharedGroupsNames() {
return invisibleSharedGroups;
}
void setInvisibleSharedGroupsNames(Set<String> groupsNames) {
invisibleSharedGroups = groupsNames;
}
/** /**
* Adds a new group to the shared groups list. * Adds a new group to the shared groups list.
* *
......
...@@ -22,9 +22,12 @@ import org.jivesoftware.wildfire.auth.UnauthorizedException; ...@@ -22,9 +22,12 @@ import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule; import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.event.GroupEventDispatcher; import org.jivesoftware.wildfire.event.GroupEventDispatcher;
import org.jivesoftware.wildfire.event.GroupEventListener; import org.jivesoftware.wildfire.event.GroupEventListener;
import org.jivesoftware.wildfire.event.UserEventDispatcher;
import org.jivesoftware.wildfire.event.UserEventListener;
import org.jivesoftware.wildfire.group.Group; import org.jivesoftware.wildfire.group.Group;
import org.jivesoftware.wildfire.group.GroupManager; import org.jivesoftware.wildfire.group.GroupManager;
import org.jivesoftware.wildfire.group.GroupNotFoundException; import org.jivesoftware.wildfire.group.GroupNotFoundException;
import org.jivesoftware.wildfire.user.User;
import org.jivesoftware.wildfire.user.UserManager; import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException; import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
...@@ -43,7 +46,7 @@ import java.util.*; ...@@ -43,7 +46,7 @@ import java.util.*;
* *
* @author Iain Shigeoka * @author Iain Shigeoka
*/ */
public class RosterManager extends BasicModule implements GroupEventListener { public class RosterManager extends BasicModule implements GroupEventListener, UserEventListener {
private Cache<String, Roster> rosterCache = null; private Cache<String, Roster> rosterCache = null;
private XMPPServer server; private XMPPServer server;
...@@ -189,6 +192,24 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -189,6 +192,24 @@ public class RosterManager extends BasicModule implements GroupEventListener {
return answer; return answer;
} }
/**
* Returns the list of shared groups whose visibility is public.
*
* @return the list of shared groups whose visibility is public.
*/
public Collection<Group> getPublicSharedGroups() {
Collection<Group> answer = new HashSet<Group>();
Collection<Group> groups = GroupManager.getInstance().getSharedGroups();
for (Group group : groups) {
String showInRoster = group.getProperties().get("sharedRoster.showInRoster");
if ("everybody".equals(showInRoster)) {
// Anyone can see this group so add the group to the answer
answer.add(group);
}
}
return answer;
}
/** /**
* Returns a collection of Groups obtained by parsing a comma delimited String with the name * Returns a collection of Groups obtained by parsing a comma delimited String with the name
* of groups. * of groups.
...@@ -242,7 +263,7 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -242,7 +263,7 @@ public class RosterManager extends BasicModule implements GroupEventListener {
public void groupModified(Group group, Map params) { public void groupModified(Group group, Map params) {
// Do nothing if no group property has been modified // Do nothing if no group property has been modified
if (!"propertyModified".equals(params.get("type"))) { if ("propertyDeleted".equals(params.get("type"))) {
return; return;
} }
String keyChanged = (String) params.get("propertyKey"); String keyChanged = (String) params.get("propertyKey");
...@@ -468,6 +489,92 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -468,6 +489,92 @@ public class RosterManager extends BasicModule implements GroupEventListener {
} }
} }
/**
* A new user has been created so members of public shared groups need to have
* their rosters updated. Members of public shared groups need to have a roster
* item with subscription FROM for the new user since the new user can see them.
*
* @param newUser the newly created user.
* @param params event parameters.
*/
public void userCreated(User newUser, Map params) {
JID newUserJID = server.createJID(newUser.getUsername(), null);
// Shared public groups that are public should have a presence subscription
// of type FROM for the new user
for (Group group : getPublicSharedGroups()) {
// Get group members of public group
Collection<JID> users = new HashSet<JID>(group.getMembers());
users.addAll(group.getAdmins());
// Update the roster of each group member to include a subscription of type FROM
for (JID userToUpdate : users) {
// Get the roster to update
Roster roster = null;
if (server.isLocal(userToUpdate)) {
// Check that the user exists, if not then continue with the next user
try {
UserManager.getInstance().getUser(userToUpdate.getNode());
}
catch (UserNotFoundException e) {
continue;
}
roster = (Roster) CacheManager.getCache("Roster").get(userToUpdate.getNode());
}
// Only update rosters in memory
if (roster != null) {
roster.addSharedUser(group, newUserJID);
}
if (!server.isLocal(userToUpdate)) {
// Susbcribe to the presence of the remote user. This is only necessary for
// remote users and may only work with remote users that **automatically**
// accept presence subscription requests
sendSubscribeRequest(newUserJID, userToUpdate, true);
}
}
}
}
public void userDeleting(User user, Map params) {
// Shared public groups that have a presence subscription of type FROM
// for the deleted user should no longer have a reference to the deleted user
JID userJID = server.createJID(user.getUsername(), null);
// Shared public groups that are public should have a presence subscription
// of type FROM for the new user
for (Group group : getPublicSharedGroups()) {
// Get group members of public group
Collection<JID> users = new HashSet<JID>(group.getMembers());
users.addAll(group.getAdmins());
// Update the roster of each group member to include a subscription of type FROM
for (JID userToUpdate : users) {
// Get the roster to update
Roster roster = null;
if (server.isLocal(userToUpdate)) {
// Check that the user exists, if not then continue with the next user
try {
UserManager.getInstance().getUser(userToUpdate.getNode());
}
catch (UserNotFoundException e) {
continue;
}
roster = (Roster) CacheManager.getCache("Roster").get(userToUpdate.getNode());
}
// Only update rosters in memory
if (roster != null) {
roster.deleteSharedUser(group, userJID);
}
if (!server.isLocal(userToUpdate)) {
// Unsusbcribe from the presence of the remote user. This is only necessary for
// remote users and may only work with remote users that **automatically**
// accept presence subscription requests
sendSubscribeRequest(userJID, userToUpdate, false);
}
}
}
}
public void userModified(User user, Map params) {
//Do nothing
}
/** /**
* Notification that a Group user has been added. Update the group users' roster accordingly. * Notification that a Group user has been added. Update the group users' roster accordingly.
* *
...@@ -830,4 +937,17 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -830,4 +937,17 @@ public class RosterManager extends BasicModule implements GroupEventListener {
} }
return false; return false;
} }
public void start() throws IllegalStateException {
super.start();
// Add this module as a user event listener so we can update
// rosters when users are created or deleted
UserEventDispatcher.addListener(this);
}
public void stop() {
super.stop();
// Remove this module as a user event listener
UserEventDispatcher.removeListener(this);
}
} }
\ No newline at end of file
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