Commit 0eb7a17f authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gaston

Added shared groups support. JM-22


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@772 b35dd754-fafc-0310-a699-88a17e54d16e
parent 162dded8
...@@ -385,10 +385,10 @@ public class DefaultGroupProvider implements GroupProvider { ...@@ -385,10 +385,10 @@ public class DefaultGroupProvider implements GroupProvider {
try { try {
con = DbConnectionManager.getConnection(); con = DbConnectionManager.getConnection();
if (adminsOnly) { if (adminsOnly) {
pstmt = con.prepareStatement(LOAD_MEMBERS); pstmt = con.prepareStatement(LOAD_ADMINS);
} }
else { else {
pstmt = con.prepareStatement(LOAD_ADMINS); pstmt = con.prepareStatement(LOAD_MEMBERS);
} }
pstmt.setString(1, groupName); pstmt.setString(1, groupName);
ResultSet rs = pstmt.executeQuery(); ResultSet rs = pstmt.executeQuery();
......
...@@ -161,6 +161,16 @@ public class Group implements Cacheable { ...@@ -161,6 +161,16 @@ public class Group implements Cacheable {
return new MemberCollection(members, false); return new MemberCollection(members, false);
} }
/**
* Returns true if the provided username belongs to a user of the group.
*
* @param username the username to check.
* @return true if the provided username belongs to a user of the group.
*/
public boolean isUser(String username) {
return members.contains(username) || administrators.contains(username);
}
public int getCachedSize() { public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size // Approximate the size of the object in bytes by calculating the size
// of each field. // of each field.
......
...@@ -102,6 +102,11 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -102,6 +102,11 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
} }
return returnPacket; return returnPacket;
} }
catch (SharedGroupException e) {
IQ result = IQ.createResultIQ(packet);
result.setError(PacketError.Condition.not_acceptable);
return result;
}
catch (Exception e) { catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
...@@ -116,7 +121,8 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -116,7 +121,8 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
* *
* @param packet The packet suspected of containing a roster removal * @param packet The packet suspected of containing a roster removal
*/ */
private void removeRosterItem(org.xmpp.packet.Roster packet) throws UnauthorizedException { private void removeRosterItem(org.xmpp.packet.Roster packet) throws UnauthorizedException,
SharedGroupException {
JID recipientJID = packet.getTo(); JID recipientJID = packet.getTo();
JID senderJID = packet.getFrom(); JID senderJID = packet.getFrom();
try { try {
...@@ -124,7 +130,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -124,7 +130,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
if (packetItem.getSubscription() == org.xmpp.packet.Roster.Subscription.remove) { if (packetItem.getSubscription() == org.xmpp.packet.Roster.Subscription.remove) {
Roster roster = userManager.getUser(recipientJID.getNode()).getRoster(); Roster roster = userManager.getUser(recipientJID.getNode()).getRoster();
RosterItem item = roster.getRosterItem(senderJID); RosterItem item = roster.getRosterItem(senderJID);
roster.deleteRosterItem(senderJID); roster.deleteRosterItem(senderJID, true);
item.setSubStatus(RosterItem.SUB_REMOVE); item.setSubStatus(RosterItem.SUB_REMOVE);
item.setSubStatus(RosterItem.SUB_NONE); item.setSubStatus(RosterItem.SUB_NONE);
...@@ -148,7 +154,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -148,7 +154,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
* @return Either a response to the roster update or null if the packet is corrupt and the session was closed down * @return Either a response to the roster update or null if the packet is corrupt and the session was closed down
*/ */
private IQ manageRoster(org.xmpp.packet.Roster packet) throws UnauthorizedException, private IQ manageRoster(org.xmpp.packet.Roster packet) throws UnauthorizedException,
UserAlreadyExistsException { UserAlreadyExistsException, SharedGroupException {
IQ returnPacket = null; IQ returnPacket = null;
Session session = sessionManager.getSession(packet.getFrom()); Session session = sessionManager.getSession(packet.getFrom());
...@@ -207,15 +213,15 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -207,15 +213,15 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
* @param item The removal item element * @param item The removal item element
*/ */
private void removeItem(org.jivesoftware.messenger.roster.Roster roster, JID sender, private void removeItem(org.jivesoftware.messenger.roster.Roster roster, JID sender,
org.xmpp.packet.Roster.Item item) { org.xmpp.packet.Roster.Item item) throws SharedGroupException {
JID recipient = item.getJID(); JID recipient = item.getJID();
// Remove recipient from the sender's roster // Remove recipient from the sender's roster
roster.deleteRosterItem(item.getJID()); roster.deleteRosterItem(item.getJID(), true);
// Forward set packet to the subscriber // Forward set packet to the subscriber
if (localServer.isLocal(recipient)) { // Recipient is local so let's handle it here if (localServer.isLocal(recipient)) { // Recipient is local so let's handle it here
try { try {
Roster recipientRoster = userManager.getUser(recipient.getNode()).getRoster(); Roster recipientRoster = userManager.getUser(recipient.getNode()).getRoster();
recipientRoster.deleteRosterItem(sender); recipientRoster.deleteRosterItem(sender, true);
} }
catch (UserNotFoundException e) { catch (UserNotFoundException e) {
} }
......
...@@ -23,6 +23,7 @@ import org.jivesoftware.messenger.user.*; ...@@ -23,6 +23,7 @@ import org.jivesoftware.messenger.user.*;
import org.xmpp.packet.Presence; import org.xmpp.packet.Presence;
import org.xmpp.packet.Packet; import org.xmpp.packet.Packet;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.Map;
...@@ -88,14 +89,14 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand ...@@ -88,14 +89,14 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand
JID recipientJID = presence.getTo(); JID recipientJID = presence.getTo();
Presence.Type type = presence.getType(); Presence.Type type = presence.getType();
Roster roster = getRoster(senderJID); Roster roster = getRoster(senderJID);
if (roster != null) {
manageSub(recipientJID, true, type, roster);
}
roster = getRoster(recipientJID);
if (roster != null) {
manageSub(senderJID, false, type, roster);
}
try { try {
if (roster != null) {
manageSub(recipientJID, true, type, roster);
}
roster = getRoster(recipientJID);
if (roster != null) {
manageSub(senderJID, false, type, roster);
}
// Try to obtain a handler for the packet based on the routes. If the handler is // Try to obtain a handler for the packet based on the routes. If the handler is
// a module, the module will be able to handle the packet. If the handler is a // a module, the module will be able to handle the packet. If the handler is a
// Session the packet will be routed to the client. If a route cannot be found // Session the packet will be routed to the client. If a route cannot be found
...@@ -106,6 +107,14 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand ...@@ -106,6 +107,14 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand
catch (NoSuchRouteException e) { catch (NoSuchRouteException e) {
deliverer.deliver(presence.createCopy()); deliverer.deliver(presence.createCopy());
} }
catch (SharedGroupException e) {
Presence result = presence.createCopy();
JID sender = result.getFrom();
result.setFrom(presence.getTo());
result.setTo(sender);
result.setError(PacketError.Condition.not_acceptable);
deliverer.deliver(result);
}
} }
catch (Exception e) { catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
...@@ -126,9 +135,14 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand ...@@ -126,9 +135,14 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand
// Check for a cached roster: // Check for a cached roster:
roster = (Roster)CacheManager.getCache("username2roster").get(username); roster = (Roster)CacheManager.getCache("username2roster").get(username);
if (roster == null) { if (roster == null) {
// Not in cache so load a new one: synchronized(address.toString().intern()) {
roster = new Roster(username); roster = (Roster)CacheManager.getCache("username2roster").get(username);
CacheManager.getCache("username2roster").put(username, roster); if (roster == null) {
// Not in cache so load a new one:
roster = new Roster(username);
CacheManager.getCache("username2roster").put(username, roster);
}
}
} }
} }
return roster; return roster;
...@@ -143,8 +157,8 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand ...@@ -143,8 +157,8 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand
* @param isSending True if the request is being sent by the owner * @param isSending True if the request is being sent by the owner
* @param type The subscription change type (subscribe, unsubscribe, etc.) * @param type The subscription change type (subscribe, unsubscribe, etc.)
*/ */
private void manageSub(JID target, boolean isSending, Presence.Type type, private void manageSub(JID target, boolean isSending, Presence.Type type, Roster roster)
Roster roster) throws UserAlreadyExistsException throws UserAlreadyExistsException, SharedGroupException
{ {
try { try {
RosterItem item; RosterItem item;
......
...@@ -14,9 +14,14 @@ package org.jivesoftware.messenger.roster; ...@@ -14,9 +14,14 @@ package org.jivesoftware.messenger.roster;
import org.jivesoftware.messenger.auth.UnauthorizedException; import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.UserNotFoundException; import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.user.UserAlreadyExistsException; import org.jivesoftware.messenger.user.UserAlreadyExistsException;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.*; import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.group.GroupManager;
import org.jivesoftware.messenger.group.Group;
import org.jivesoftware.util.Cacheable; import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.CacheSizes; import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Log;
import org.xmpp.packet.*; import org.xmpp.packet.*;
import java.util.*; import java.util.*;
...@@ -50,20 +55,62 @@ public class Roster implements Cacheable { ...@@ -50,20 +55,62 @@ public class Roster implements Cacheable {
/** /**
* <p>Create a roster for the given user, pulling the existing roster items * <p>Create a roster for the given user, pulling the existing roster items
* out of the backend storage provider.</p> * out of the backend storage provider. The roster will also include items that
* belong to the user's shared groups.</p>
*
* <p>RosterItems that ONLY belong to shared groups won't be persistent unless the user
* explicitly subscribes to the contact's presence, renames the contact in his roster or adds
* the item to a personal group.</>
* *
* @param username The username of the user that owns this roster * @param username The username of the user that owns this roster
*/ */
public Roster(String username) { public Roster(String username) {
sessionManager = SessionManager.getInstance(); sessionManager = SessionManager.getInstance();
this.username = username; this.username = username;
// Get the shared groups of this user
Collection<Group> sharedGroups = null;
try {
User rosterUser = UserManager.getInstance().getUser(getUsername());
sharedGroups = GroupManager.getInstance().getGroups(rosterUser);
}
catch (UserNotFoundException e) {
sharedGroups = new ArrayList<Group>();
}
// Add RosterItems that belong to the personal roster
rosterItemProvider = RosterItemProvider.getInstance(); rosterItemProvider = RosterItemProvider.getInstance();
Iterator items = rosterItemProvider.getItems(username); Iterator items = rosterItemProvider.getItems(username);
while (items.hasNext()) { while (items.hasNext()) {
RosterItem item = (RosterItem)items.next(); RosterItem item = (RosterItem)items.next();
// Check if the item (i.e. contact) belongs to a shared group of the user. Add the
// shared group (if any) to this item
for (Group group : sharedGroups) {
if (group.isUser(item.getJid().getNode())) {
// TODO Group name conflicts are not being considered (do we need this?)
item.addSharedGroups(group.getName());
}
}
rosterItems.put(item.getJid().toBareJID(), item); rosterItems.put(item.getJid().toBareJID(), item);
} }
// Add RosterItems that belong only to shared groups
Map<JID,List<String>> sharedUsers = getSharedUsers(sharedGroups);
for (JID jid : sharedUsers.keySet()) {
try {
User user = UserManager.getInstance().getUser(jid.getNode());
RosterItem item = new RosterItem(jid, RosterItem.SUB_BOTH, RosterItem.ASK_NONE,
RosterItem.RECV_NONE, user.getName(), null);
for (String group : sharedUsers.get(jid)) {
item.addSharedGroups(group);
}
rosterItems.put(item.getJid().toBareJID(), item);
}
catch (UserNotFoundException e) {
Log.error("Groups (" + sharedUsers.get(jid) + ") include non-existent username (" +
jid.getNode() +
")");
}
}
} }
/** /**
...@@ -116,7 +163,8 @@ public class Roster implements Cacheable { ...@@ -116,7 +163,8 @@ public class Roster implements Cacheable {
* *
* @param user the item to add to the roster. * @param user the item to add to the roster.
*/ */
public RosterItem createRosterItem(JID user) throws UserAlreadyExistsException { public RosterItem createRosterItem(JID user) throws UserAlreadyExistsException,
SharedGroupException {
return createRosterItem(user, null, null); return createRosterItem(user, null, null);
} }
...@@ -129,7 +177,7 @@ public class Roster implements Cacheable { ...@@ -129,7 +177,7 @@ public class Roster implements Cacheable {
* @param groups The list of groups to assign this roster item to (can be null) * @param groups The list of groups to assign this roster item to (can be null)
*/ */
public RosterItem createRosterItem(JID user, String nickname, List<String> groups) public RosterItem createRosterItem(JID user, String nickname, List<String> groups)
throws UserAlreadyExistsException { throws UserAlreadyExistsException, SharedGroupException {
RosterItem item = provideRosterItem(user, nickname, groups); RosterItem item = provideRosterItem(user, nickname, groups);
rosterItems.put(item.getJid().toBareJID(), item); rosterItems.put(item.getJid().toBareJID(), item);
return item; return item;
...@@ -143,7 +191,7 @@ public class Roster implements Cacheable { ...@@ -143,7 +191,7 @@ public class Roster implements Cacheable {
* @param item the item to copy and add to the roster. * @param item the item to copy and add to the roster.
*/ */
public void createRosterItem(org.xmpp.packet.Roster.Item item) public void createRosterItem(org.xmpp.packet.Roster.Item item)
throws UnauthorizedException, UserAlreadyExistsException { throws UserAlreadyExistsException, SharedGroupException {
RosterItem rosterItem = provideRosterItem(item); RosterItem rosterItem = provideRosterItem(item);
rosterItems.put(item.getJID().toBareJID(), rosterItem); rosterItems.put(item.getJID().toBareJID(), rosterItem);
} }
...@@ -155,7 +203,7 @@ public class Roster implements Cacheable { ...@@ -155,7 +203,7 @@ public class Roster implements Cacheable {
* @return The newly created roster items ready to be stored by the Roster item's hash table * @return The newly created roster items ready to be stored by the Roster item's hash table
*/ */
protected RosterItem provideRosterItem(org.xmpp.packet.Roster.Item item) protected RosterItem provideRosterItem(org.xmpp.packet.Roster.Item item)
throws UserAlreadyExistsException, UnauthorizedException { throws UserAlreadyExistsException, SharedGroupException {
return provideRosterItem(item.getJID(), item.getName(), return provideRosterItem(item.getJID(), item.getName(),
new ArrayList<String>(item.getGroups())); new ArrayList<String>(item.getGroups()));
} }
...@@ -169,7 +217,18 @@ public class Roster implements Cacheable { ...@@ -169,7 +217,18 @@ public class Roster implements Cacheable {
* @return The newly created roster items ready to be stored by the Roster item's hash table * @return The newly created roster items ready to be stored by the Roster item's hash table
*/ */
protected RosterItem provideRosterItem(JID user, String nickname, List<String> groups) protected RosterItem provideRosterItem(JID user, String nickname, List<String> groups)
throws UserAlreadyExistsException { throws UserAlreadyExistsException, SharedGroupException {
if (groups != null && !groups.isEmpty()) {
// Raise an error if the groups the item belongs to include a shared group
Collection<Group> sharedGroups = GroupManager.getInstance().getGroups();
for (String group : groups) {
for (Group sharedGroup : sharedGroups) {
if (sharedGroup.getName().equals(group)) {
throw new SharedGroupException("Cannot add an item to a shared group");
}
}
}
}
org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster(); org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
roster.setType(IQ.Type.set); roster.setType(IQ.Type.set);
org.xmpp.packet.Roster.Item item = roster.addItem(user, nickname, null, org.xmpp.packet.Roster.Item item = roster.addItem(user, nickname, null,
...@@ -194,18 +253,33 @@ public class Roster implements Cacheable { ...@@ -194,18 +253,33 @@ public class Roster implements Cacheable {
rosterItems.remove(item.getJid().toBareJID()); rosterItems.remove(item.getJid().toBareJID());
throw new UserNotFoundException(item.getJid().toBareJID()); throw new UserNotFoundException(item.getJid().toBareJID());
} }
// Update the backend data store // If the item only had shared groups before this update then make it persistent
rosterItemProvider.updateItem(username, item); if (item.isShared() && item.getID() == 0) {
try {
rosterItemProvider.createItem(username, item);
}
catch (UserAlreadyExistsException e) {
// Do nothing. We shouldn't be here.
}
}
else {
// Update the backend data store
rosterItemProvider.updateItem(username, item);
}
// broadcast roster update // broadcast roster update
if (!(item.getSubStatus() == RosterItem.SUB_NONE if (!(item.getSubStatus() == RosterItem.SUB_NONE
&& item.getAskStatus() == RosterItem.ASK_NONE)) { && item.getAskStatus() == RosterItem.ASK_NONE)) {
// Set the groups to broadcast (include personal and shared groups)
List<String> groups = new ArrayList<String>(item.getGroups());
groups.addAll(item.getSharedGroups());
org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster(); org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
roster.setType(IQ.Type.set); roster.setType(IQ.Type.set);
roster.addItem(item.getJid(), item.getNickname(), roster.addItem(item.getJid(), item.getNickname(),
getAskStatus(item.getAskStatus()), getAskStatus(item.getAskStatus()),
org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus().getName()), org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus().getName()),
item.getGroups()); groups);
broadcast(roster); broadcast(roster);
} }
...@@ -222,14 +296,26 @@ public class Roster implements Cacheable { ...@@ -222,14 +296,26 @@ public class Roster implements Cacheable {
* Remove a user from the roster. * Remove a user from the roster.
* *
* @param user the user to remove from the roster. * @param user the user to remove from the roster.
* @param forceRemove flag that indicates if checkings should be done before deleting the user.
* @return The roster item being removed or null if none existed * @return The roster item being removed or null if none existed
* @throws SharedGroupException if the user to remove belongs to a shared group
*/ */
public RosterItem deleteRosterItem(JID user) { public RosterItem deleteRosterItem(JID user, boolean forceRemove) throws SharedGroupException {
// Answer an error if user (i.e. contact) to delete belongs to a shared group
RosterItem itemToRemove = rosterItems.get(user.toBareJID());
if (itemToRemove.isShared()) {
throw new SharedGroupException("Cannot remove contact that belongs to a shared group");
}
// If removing the user was successful, remove the user from the subscriber list: // If removing the user was successful, remove the user from the subscriber list:
RosterItem item = rosterItems.remove(user.toBareJID()); RosterItem item = rosterItems.remove(user.toBareJID());
if (item != null) { if (item != null) {
// If removing the user was successful, remove the user from the backend store // Delete the item from the provider if the item is persistent. RosteItems that only
rosterItemProvider.deleteItem(username, item.getID()); // belong to shared groups won't be persistent
if (item.getID() > 0) {
// If removing the user was successful, remove the user from the backend store
rosterItemProvider.deleteItem(username, item.getID());
}
// Broadcast the update to the user // Broadcast the update to the user
org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster(); org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
...@@ -257,19 +343,25 @@ public class Roster implements Cacheable { ...@@ -257,19 +343,25 @@ public class Roster implements Cacheable {
*/ */
public org.xmpp.packet.Roster getReset() { public org.xmpp.packet.Roster getReset() {
org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster(); org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
// Add the roster items (includes the personal roster and shared groups) to the answer
for (RosterItem item : rosterItems.values()) { for (RosterItem item : rosterItems.values()) {
if (item.getSubStatus() != RosterItem.SUB_NONE || item.getAskStatus() != RosterItem.ASK_NONE) { org.xmpp.packet.Roster.Ask ask = getAskStatus(item.getAskStatus());
roster.addItem(item.getJid(), item.getNickname(), org.xmpp.packet.Roster.Subscription sub = org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus()
getAskStatus(item.getAskStatus()), .getName());
org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus().getName()), // Set the groups to broadcast (include personal and shared groups)
item.getGroups()); List<String> groups = new ArrayList<String>(item.getGroups());
groups.addAll(item.getSharedGroups());
if (item.getSubStatus() != RosterItem.SUB_NONE ||
item.getAskStatus() != RosterItem.ASK_NONE) {
roster.addItem(item.getJid(), item.getNickname(), ask, sub, groups);
} }
} }
return roster; return roster;
} }
private org.xmpp.packet.Roster.Ask getAskStatus(RosterItem.AskType askType) { private org.xmpp.packet.Roster.Ask getAskStatus(RosterItem.AskType askType) {
if ("".equals(askType.getName())) { if (askType == null || "".equals(askType.getName())) {
return null; return null;
} }
return org.xmpp.packet.Roster.Ask.valueOf(askType.getName()); return org.xmpp.packet.Roster.Ask.valueOf(askType.getName());
...@@ -309,6 +401,41 @@ public class Roster implements Cacheable { ...@@ -309,6 +401,41 @@ public class Roster implements Cacheable {
} }
} }
/**
* Returns the list of users that belong ONLY to a shared group of this user. If the contact
* belongs to the personal roster and a shared group then it wont' be included in the answer.
*
* @param sharedGroups the shared groups of this user.
* @return the list of users that belong ONLY to a shared group of this user.
*/
private Map<JID,List<String>> getSharedUsers(Collection<Group> sharedGroups) {
// Get the users to process from the shared groups. Users that belong to different groups
// will have one entry in the map associated with all the groups
Map<JID,List<String>> sharedGroupUsers = new HashMap<JID,List<String>>();
for (Group group : sharedGroups) {
// Collect all the users of the group (i.e. members and administrators)
Collection<String> users = new ArrayList<String>(group.getAdministrators());
users.addAll(group.getMembers());
// Add the users of the group to the general list of users to process
for (String groupUser : users) {
// Add the user to the answer if the user doesn't belong to the personal roster
// (since we have already added the user to the answer)
JID jid = new JID(groupUser, XMPPServer.getInstance().getServerInfo().getName(),
null);
if (!isRosterItem(jid) && !getUsername().equals(groupUser)) {
List<String> groups = sharedGroupUsers.get(jid);
if (groups == null) {
groups = new ArrayList<String>();
sharedGroupUsers.put(jid, groups);
}
groups.add(group.getName());
}
}
}
return sharedGroupUsers;
}
private void broadcast(org.xmpp.packet.Roster roster) { private void broadcast(org.xmpp.packet.Roster roster) {
if (server == null) { if (server == null) {
server = XMPPServer.getInstance(); server = XMPPServer.getInstance();
......
...@@ -14,11 +14,12 @@ package org.jivesoftware.messenger.roster; ...@@ -14,11 +14,12 @@ package org.jivesoftware.messenger.roster;
import org.jivesoftware.util.IntEnum; import org.jivesoftware.util.IntEnum;
import org.jivesoftware.util.Cacheable; import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.CacheSizes; import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.messenger.group.GroupManager;
import org.jivesoftware.messenger.group.GroupNotFoundException;
import org.jivesoftware.messenger.SharedGroupException;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.util.List; import java.util.*;
import java.util.LinkedList;
import java.util.Iterator;
/** /**
* <p>Represents a single roster item for a User's Roster.</p> * <p>Represents a single roster item for a User's Roster.</p>
...@@ -123,6 +124,7 @@ public class RosterItem implements Cacheable { ...@@ -123,6 +124,7 @@ public class RosterItem implements Cacheable {
protected JID jid; protected JID jid;
protected String nickname; protected String nickname;
protected List<String> groups; protected List<String> groups;
protected Set<String> sharedGroups = new HashSet<String>();
protected SubType subStatus; protected SubType subStatus;
protected AskType askStatus; protected AskType askStatus;
private long rosterID; private long rosterID;
...@@ -151,9 +153,8 @@ public class RosterItem implements Cacheable { ...@@ -151,9 +153,8 @@ public class RosterItem implements Cacheable {
this.nickname = nickname; this.nickname = nickname;
this.groups = new LinkedList<String>(); this.groups = new LinkedList<String>();
if (groups != null) { if (groups != null) {
Iterator<String> groupItr = groups.iterator(); for (String group : groups) {
while (groupItr.hasNext()) { this.groups.add(group);
this.groups.add(groupItr.next());
} }
} }
} }
...@@ -208,7 +209,13 @@ public class RosterItem implements Cacheable { ...@@ -208,7 +209,13 @@ public class RosterItem implements Cacheable {
* @return The subscription status of the item * @return The subscription status of the item
*/ */
public SubType getSubStatus() { public SubType getSubStatus() {
return subStatus; if (isShared()) {
// Redefine the sub status since the item belongs to a shared group
return SUB_BOTH;
}
else {
return subStatus;
}
} }
/** /**
...@@ -226,7 +233,13 @@ public class RosterItem implements Cacheable { ...@@ -226,7 +233,13 @@ public class RosterItem implements Cacheable {
* @return The ask status of the item * @return The ask status of the item
*/ */
public AskType getAskStatus() { public AskType getAskStatus() {
return askStatus; if (isShared()) {
// Redefine the ask status since the item belongs to a shared group
return ASK_NONE;
}
else {
return askStatus;
}
} }
/** /**
...@@ -284,9 +297,9 @@ public class RosterItem implements Cacheable { ...@@ -284,9 +297,9 @@ public class RosterItem implements Cacheable {
} }
/** /**
* <p>Obtain the groups for the item.</p> * Returns the groups for the item. Shared groups won't be included in the answer.
* *
* @return The subscription status of the item * @return The groups for the item.
*/ */
public List<String> getGroups() { public List<String> getGroups() {
return groups; return groups;
...@@ -295,17 +308,77 @@ public class RosterItem implements Cacheable { ...@@ -295,17 +308,77 @@ public class RosterItem implements Cacheable {
/** /**
* <p>Set the current groups for the item.</p> * <p>Set the current groups for the item.</p>
* *
* @param groups The subscription status of the item * @param groups The new lists of groups the item belongs to.
*/ */
public void setGroups(List<String> groups) { public void setGroups(List<String> groups) throws SharedGroupException {
if (groups == null) { if (groups == null) {
this.groups = new LinkedList<String>(); this.groups = new LinkedList<String>();
} }
else { else {
// Raise an error if the user is trying to remove the item from a shared group
for (String groupName : getSharedGroups()) {
// Check if the group has been removed from the new groups list
if (!groups.contains(groupName)) {
throw new SharedGroupException("Cannot remove item from shared group");
}
}
// Remove shared groups from the param
for (Iterator<String> it=groups.iterator(); it.hasNext();) {
try {
String group = it.next();
// Check if exists a shared group with this name
GroupManager.getInstance().getGroup(group);
// Remove the shared group from the list (since it exists)
it.remove();
}
catch (GroupNotFoundException e) {
// Do nothing since the group is a personal group
}
}
this.groups = groups; this.groups = groups;
} }
} }
/**
* Returns the shared groups for the item.
*
* @return The shared groups this item belongs to.
*/
public Collection<String> getSharedGroups() {
return sharedGroups;
}
/**
* Adds a new group to the shared groups list.
*
* @param sharedGroup The shared group to add to the list of shared groups.
*/
public void addSharedGroups(String sharedGroup) {
sharedGroups.add(sharedGroup);
}
/**
* Returns true if this item belongs to a shared group. Return true even if the item belongs
* to a personal group and a shared group.
*
* @return true if this item belongs to a shared group.
*/
public boolean isShared() {
return !sharedGroups.isEmpty();
}
/**
* Returns true if this item belongs ONLY to shared groups. This means that the the item is
* considered to be "only shared" if it doesn't belong to a personal group but only to shared
* groups.
*
* @return true if this item belongs ONLY to shared groups.
*/
public boolean isOnlyShared() {
return !sharedGroups.isEmpty() && groups.isEmpty();
}
/** /**
* <p>Obtain the roster ID associated with this particular roster item.</p> * <p>Obtain the roster ID associated with this particular roster item.</p>
* <p/> * <p/>
...@@ -328,9 +401,9 @@ public class RosterItem implements Cacheable { ...@@ -328,9 +401,9 @@ public class RosterItem implements Cacheable {
* *
* @param item The item who's settings will be copied into the cached copy * @param item The item who's settings will be copied into the cached copy
*/ */
public void setAsCopyOf(org.xmpp.packet.Roster.Item item) { public void setAsCopyOf(org.xmpp.packet.Roster.Item item) throws SharedGroupException {
setNickname(item.getName());
setGroups(new LinkedList<String>(item.getGroups())); setGroups(new LinkedList<String>(item.getGroups()));
setNickname(item.getName());
} }
public int getCachedSize() { public int getCachedSize() {
......
...@@ -16,6 +16,7 @@ import org.jivesoftware.util.Cache; ...@@ -16,6 +16,7 @@ import org.jivesoftware.util.Cache;
import org.jivesoftware.util.CacheManager; import org.jivesoftware.util.CacheManager;
import org.jivesoftware.messenger.container.BasicModule; import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.user.UserNotFoundException; import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.SharedGroupException;
import java.util.Iterator; import java.util.Iterator;
...@@ -82,7 +83,12 @@ public class RosterManager extends BasicModule { ...@@ -82,7 +83,12 @@ public class RosterManager extends BasicModule {
} }
// Remove each roster item from the user's roster // Remove each roster item from the user's roster
for (RosterItem item : roster.getRosterItems()) { for (RosterItem item : roster.getRosterItems()) {
roster.deleteRosterItem(item.getJid()); try {
roster.deleteRosterItem(item.getJid(), false);
}
catch (SharedGroupException e) {
// Do nothing. We shouldn't have this exception since we disabled the checkings
}
} }
// Remove the cached roster from memory // Remove the cached roster from memory
CacheManager.getCache("username2roster").remove(username); CacheManager.getCache("username2roster").remove(username);
...@@ -99,7 +105,12 @@ public class RosterManager extends BasicModule { ...@@ -99,7 +105,12 @@ public class RosterManager extends BasicModule {
roster = new Roster(username); roster = new Roster(username);
} }
// Remove the deleted user reference from this roster // Remove the deleted user reference from this roster
roster.deleteRosterItem(user); try {
roster.deleteRosterItem(user, false);
}
catch (SharedGroupException e) {
// Do nothing. We shouldn't have this exception since we disabled the checkings
}
} }
} }
catch (UnsupportedOperationException e) { catch (UnsupportedOperationException e) {
......
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