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 {
protected ConcurrentHashMap<String, RosterItem> rosterItems = new ConcurrentHashMap<String, RosterItem>();
/**
* 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 String username;
......@@ -153,7 +153,8 @@ public class Roster implements Cacheable {
}
else {
// 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) {
......@@ -220,9 +221,12 @@ public class Roster implements Cacheable {
* user and the susbcription only exists due to some shared groups or otherwise null.
*/
private RosterItem getImplicitRosterItem(JID user) {
if (implicitFrom.containsKey(user.toBareJID())) {
return new RosterItem(user, RosterItem.SUB_FROM, RosterItem.ASK_NONE,
Set<String> invisibleSharedGroups = implicitFrom.get(user.toBareJID());
if (invisibleSharedGroups != null) {
RosterItem rosterItem = new RosterItem(user, RosterItem.SUB_FROM, RosterItem.ASK_NONE,
RosterItem.RECV_NONE, "", null);
rosterItem.setInvisibleSharedGroupsNames(invisibleSharedGroups);
return rosterItem;
}
return null;
}
......@@ -325,6 +329,11 @@ public class Roster implements Cacheable {
* @throws UserNotFoundException If the roster item for the given user doesn't already exist
*/
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) {
rosterItems.remove(item.getJid().toBareJID());
if (item.getSubStatus() != RosterItem.SUB_NONE) {
......@@ -381,7 +390,7 @@ public class Roster implements Cacheable {
public RosterItem deleteRosterItem(JID user, boolean doChecking) throws SharedGroupException {
// Answer an error if user (i.e. contact) to delete belongs to a shared group
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");
}
......@@ -426,6 +435,20 @@ public class Roster implements Cacheable {
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;
}
......@@ -752,11 +775,13 @@ public class Roster implements Cacheable {
// Remove from memory and do nothing else
rosterItems.remove(item.getJid().toBareJID());
// 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 {
// Remove from list of shared contacts with status FROM (if any)
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
broadcast(item, true);
// Probe the presence of the new group user
......@@ -861,11 +886,13 @@ public class Roster implements Cacheable {
// Remove from memory and do nothing else
rosterItems.remove(item.getJid().toBareJID());
// 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 {
// Remove from list of shared contacts with status FROM (if any)
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
broadcast(item, true);
// Probe the presence of the new group user
......@@ -893,7 +920,8 @@ public class Roster implements Cacheable {
int groupSize = item.getSharedGroups().size() + item.getInvisibleSharedGroups().size();
if (item.isOnlyShared() && groupSize == 1) {
// 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;
}
// Delete the roster item from the roster since it exists only because of this
......
......@@ -349,7 +349,9 @@ public class RosterItem implements Cacheable {
try {
it.remove();
}
catch (IllegalStateException e) {}
catch (IllegalStateException e) {
// Do nothing
}
}
}
catch (GroupNotFoundException e) {
......@@ -362,7 +364,9 @@ public class RosterItem implements Cacheable {
try {
it.remove();
}
catch (IllegalStateException ise) {}
catch (IllegalStateException ise) {
// Do nothing
}
}
}
}
......@@ -383,6 +387,7 @@ public class RosterItem implements Cacheable {
groups.add(GroupManager.getInstance().getGroup(groupName));
}
catch (GroupNotFoundException e) {
// Do nothing
}
}
return groups;
......@@ -402,11 +407,20 @@ public class RosterItem implements Cacheable {
groups.add(GroupManager.getInstance().getGroup(groupName));
}
catch (GroupNotFoundException e) {
// Do nothing
}
}
return groups;
}
Set<String> getInvisibleSharedGroupsNames() {
return invisibleSharedGroups;
}
void setInvisibleSharedGroupsNames(Set<String> groupsNames) {
invisibleSharedGroups = groupsNames;
}
/**
* Adds a new group to the shared groups list.
*
......
......@@ -22,9 +22,12 @@ import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.event.GroupEventDispatcher;
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.GroupManager;
import org.jivesoftware.wildfire.group.GroupNotFoundException;
import org.jivesoftware.wildfire.user.User;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.JID;
......@@ -43,7 +46,7 @@ import java.util.*;
*
* @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 XMPPServer server;
......@@ -189,6 +192,24 @@ public class RosterManager extends BasicModule implements GroupEventListener {
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
* of groups.
......@@ -242,7 +263,7 @@ public class RosterManager extends BasicModule implements GroupEventListener {
public void groupModified(Group group, Map params) {
// Do nothing if no group property has been modified
if (!"propertyModified".equals(params.get("type"))) {
if ("propertyDeleted".equals(params.get("type"))) {
return;
}
String keyChanged = (String) params.get("propertyKey");
......@@ -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.
*
......@@ -830,4 +937,17 @@ public class RosterManager extends BasicModule implements GroupEventListener {
}
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