Commit 75f7e25a authored by Christian Schudt's avatar Christian Schudt

OF-876 IQRosterHandler does not respect error cases in RFC 6121 § 2.3.3.

parent dd853af3
...@@ -23,7 +23,9 @@ package org.jivesoftware.openfire.handler; ...@@ -23,7 +23,9 @@ package org.jivesoftware.openfire.handler;
import gnu.inet.encoding.IDNAException; import gnu.inet.encoding.IDNAException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set;
import org.jivesoftware.openfire.IQHandlerInfo; import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.PacketException; import org.jivesoftware.openfire.PacketException;
...@@ -120,8 +122,9 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -120,8 +122,9 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
returnPacket = manageRoster(roster); returnPacket = manageRoster(roster);
} else { } else {
returnPacket = IQ.createResultIQ(packet); returnPacket = IQ.createResultIQ(packet);
returnPacket.setChildElement(packet.getChildElement().createCopy()); // The server MUST return a <forbidden/> stanza error to the client if the sender of the roster set is not authorized to update the roster
returnPacket.setError(PacketError.Condition.service_unavailable); // (where typically only an authenticated resource of the account itself is authorized).
returnPacket.setError(PacketError.Condition.forbidden);
} }
return returnPacket; return returnPacket;
} }
...@@ -193,25 +196,38 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -193,25 +196,38 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
returnPacket = null; returnPacket = null;
} }
else if (IQ.Type.set == type) { else if (IQ.Type.set == type) {
returnPacket = IQ.createResultIQ(packet);
for (org.xmpp.packet.Roster.Item item : packet.getItems()) { // RFC 6121 2.3.3. Error Cases:
if (item.getSubscription() == org.xmpp.packet.Roster.Subscription.remove) { // The server MUST return a <bad-request/> stanza error to the client if the roster set contains any of the following violations:
removeItem(cachedRoster, packet.getFrom(), item); // The <query/> element contains more than one <item/> child element.
} if (packet.getItems().size() > 1) {
else { returnPacket.setError(new PacketError(PacketError.Condition.bad_request, PacketError.Type.modify, "Query contains more than one item"));
if (cachedRoster.isRosterItem(item.getJID())) { } else {
// existing item for (org.xmpp.packet.Roster.Item item : packet.getItems()) {
RosterItem cachedItem = cachedRoster.getRosterItem(item.getJID()); if (item.getSubscription() == org.xmpp.packet.Roster.Subscription.remove) {
cachedItem.setAsCopyOf(item); if (removeItem(cachedRoster, packet.getFrom(), item) == null) {
cachedRoster.updateRosterItem(cachedItem); // RFC 6121 2.5.3. Error Cases: If the value of the 'jid' attribute specifies an item that is not in the roster, then the server MUST return an <item-not-found/> stanza error.
} returnPacket.setError(PacketError.Condition.item_not_found);
else { }
// new item } else {
cachedRoster.createRosterItem(item); PacketError error = checkGroups(item.getGroups());
if (error != null) {
returnPacket.setError(error);
} else {
if (cachedRoster.isRosterItem(item.getJID())) {
// existing item
RosterItem cachedItem = cachedRoster.getRosterItem(item.getJID());
cachedItem.setAsCopyOf(item);
cachedRoster.updateRosterItem(cachedItem);
} else {
// new item
cachedRoster.createRosterItem(item);
}
}
} }
} }
} }
returnPacket = IQ.createResultIQ(packet);
} }
} }
catch (UserNotFoundException e) { catch (UserNotFoundException e) {
...@@ -222,6 +238,29 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -222,6 +238,29 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
} }
/**
* Checks the roster groups for error conditions described in RFC 6121 § 2.3.3.
*
* @param groups The groups.
* @return An error if the specification is violated or null if everything is fine.
*/
private static PacketError checkGroups(Iterable<String> groups) {
Set<String> set = new HashSet<String>();
for (String group : groups) {
if (!set.add(group)) {
// Duplicate group found.
// RFC 6121 2.3.3. Error Cases: 2. The <item/> element contains more than one <group/> element, but there are duplicate groups
return new PacketError(PacketError.Condition.bad_request, PacketError.Type.modify, "Item contains duplicate groups");
}
if (group.isEmpty()) {
// The server MUST return a <not-acceptable/> stanza error to the client if the roster set contains any of the following violations:
// 2. The XML character data of the <group/> element is of zero length.
return new PacketError(PacketError.Condition.not_acceptable, PacketError.Type.modify, "Group is of zero length");
}
}
return null;
}
/** /**
* Remove the roster item from the sender's roster (and possibly the recipient's). * Remove the roster item from the sender's roster (and possibly the recipient's).
* Actual roster removal is done in the removeItem(Roster,RosterItem) method. * Actual roster removal is done in the removeItem(Roster,RosterItem) method.
...@@ -229,12 +268,14 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -229,12 +268,14 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
* @param roster The sender's roster. * @param roster The sender's roster.
* @param sender The JID of the sender of the removal request * @param sender The JID of the sender of the removal request
* @param item The removal item element * @param item The removal item element
* @return The removed item or null, if not item has been removed.
*/ */
private void removeItem(org.jivesoftware.openfire.roster.Roster roster, JID sender, private RosterItem removeItem(org.jivesoftware.openfire.roster.Roster roster, JID sender,
org.xmpp.packet.Roster.Item item) throws SharedGroupException { 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(), true); RosterItem removedItem = 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 {
...@@ -270,6 +311,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider ...@@ -270,6 +311,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
router.route(removePacket); router.route(removePacket);
} }
} }
return removedItem;
} }
/** /**
......
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