mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-12-22 20:47:57 +01:00
Added support for pre-approved subscription requests (RFC 6121 § 3.4)
SMACK-639
This commit is contained in:
parent
f410ece468
commit
fae9d129a9
12 changed files with 440 additions and 5 deletions
|
@ -1405,7 +1405,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
return getFeature(element, namespace) != null;
|
||||
}
|
||||
|
||||
private void addStreamFeature(ExtensionElement feature) {
|
||||
protected void addStreamFeature(ExtensionElement feature) {
|
||||
String key = XmppStringUtils.generateKey(feature.getElementName(), feature.getNamespace());
|
||||
streamFeatures.put(key, feature);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.BlockingQueue;
|
|||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
|
@ -191,6 +192,16 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
invokePacketCollectorsAndNotifyRecvListeners(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable stream feature.
|
||||
*
|
||||
* @param streamFeature the stream feature.
|
||||
* @since 4.2
|
||||
*/
|
||||
public void enableStreamFeature(ExtensionElement streamFeature) {
|
||||
addStreamFeature(streamFeature);
|
||||
}
|
||||
|
||||
public static DummyConnection newConnectedDummyConnection() {
|
||||
DummyConnection dummyConnection = new DummyConnection();
|
||||
try {
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.jivesoftware.smack.Manager;
|
|||
import org.jivesoftware.smack.StanzaListener;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
|
@ -57,6 +58,7 @@ import org.jivesoftware.smack.packet.XMPPError.Condition;
|
|||
import org.jivesoftware.smack.roster.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.roster.packet.RosterVer;
|
||||
import org.jivesoftware.smack.roster.packet.RosterPacket.Item;
|
||||
import org.jivesoftware.smack.roster.packet.SubscriptionPreApproval;
|
||||
import org.jivesoftware.smack.roster.rosterstore.RosterStore;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
@ -456,6 +458,7 @@ public class Roster extends Manager {
|
|||
* @param name the nickname of the user.
|
||||
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
|
||||
* the roster entry won't belong to a group.
|
||||
* @param approved the pre-approval state.
|
||||
* @throws NoResponseException if there was no response from the server.
|
||||
* @throws XMPPErrorException if an XMPP exception occurs.
|
||||
* @throws NotLoggedInException If not logged in.
|
||||
|
@ -491,6 +494,68 @@ public class Roster extends Manager {
|
|||
connection.sendStanza(presencePacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new pre-approved roster entry and presence subscription. The server will
|
||||
* asynchronously update the roster with the subscription status.
|
||||
*
|
||||
* @param user the user. (e.g. johndoe@jabber.org)
|
||||
* @param name the nickname of the user.
|
||||
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
|
||||
* the roster entry won't belong to a group.
|
||||
* @param approved the pre-approval state.
|
||||
* @throws NoResponseException if there was no response from the server.
|
||||
* @throws XMPPErrorException if an XMPP exception occurs.
|
||||
* @throws NotLoggedInException if not logged in.
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @throws FeatureNotSupportedException if pre-approving is not supported.
|
||||
* @since 4.2
|
||||
*/
|
||||
public void preApproveAndCreateEntry(Jid user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, FeatureNotSupportedException {
|
||||
preApprove(user);
|
||||
createEntry(user, name, groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-approve user presence subscription.
|
||||
*
|
||||
* @param user the user. (e.g. johndoe@jabber.org)
|
||||
* @throws NotLoggedInException if not logged in.
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @throws FeatureNotSupportedException if pre-approving is not supported.
|
||||
* @since 4.2
|
||||
*/
|
||||
public void preApprove(Jid user) throws NotLoggedInException, NotConnectedException, InterruptedException, FeatureNotSupportedException {
|
||||
final XMPPConnection connection = connection();
|
||||
if (!isSubscriptionPreApprovalSupported()) {
|
||||
throw new FeatureNotSupportedException("Pre-approving");
|
||||
}
|
||||
|
||||
Presence presencePacket = new Presence(Presence.Type.subscribed);
|
||||
presencePacket.setTo(user);
|
||||
connection.sendStanza(presencePacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for subscription pre-approval support.
|
||||
*
|
||||
* @return true if subscription pre-approval is supported by the server.
|
||||
* @throws NotLoggedInException if not logged in.
|
||||
* @since 4.2
|
||||
*/
|
||||
public boolean isSubscriptionPreApprovalSupported() throws NotLoggedInException {
|
||||
final XMPPConnection connection = connection();
|
||||
if (!connection.isAuthenticated()) {
|
||||
throw new NotLoggedInException();
|
||||
}
|
||||
if (connection.isAnonymous()) {
|
||||
throw new IllegalStateException("Anonymous users can't have a roster.");
|
||||
}
|
||||
|
||||
return connection.hasFeature(SubscriptionPreApproval.ELEMENT, SubscriptionPreApproval.NAMESPACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a roster entry from the roster. The roster entry will also be removed from the
|
||||
* unfiled entries or from any roster group where it could belong and will no longer be part
|
||||
|
@ -1329,7 +1394,7 @@ public class Roster extends Manager {
|
|||
|
||||
for (RosterPacket.Item item : validItems) {
|
||||
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
|
||||
item.getItemType(), item.getItemStatus(), Roster.this, connection);
|
||||
item.getItemType(), item.getItemStatus(), item.isApproved(), Roster.this, connection);
|
||||
addUpdateEntry(addedEntries, updatedEntries, unchangedEntries, item, entry);
|
||||
}
|
||||
|
||||
|
@ -1359,7 +1424,7 @@ public class Roster extends Manager {
|
|||
// await possible further roster pushes.
|
||||
for (RosterPacket.Item item : rosterStore.getEntries()) {
|
||||
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
|
||||
item.getItemType(), item.getItemStatus(), Roster.this, connection);
|
||||
item.getItemType(), item.getItemStatus(), item.isApproved(), Roster.this, connection);
|
||||
addUpdateEntry(addedEntries, updatedEntries, unchangedEntries, item, entry);
|
||||
}
|
||||
}
|
||||
|
@ -1429,7 +1494,7 @@ public class Roster extends Manager {
|
|||
// safely retrieve this single item here.
|
||||
Item item = items.iterator().next();
|
||||
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
|
||||
item.getItemType(), item.getItemStatus(), Roster.this, connection);
|
||||
item.getItemType(), item.getItemStatus(), item.isApproved(), Roster.this, connection);
|
||||
String version = rosterPacket.getVersion();
|
||||
|
||||
if (item.getItemType().equals(RosterPacket.ItemType.remove)) {
|
||||
|
|
|
@ -47,6 +47,7 @@ public class RosterEntry {
|
|||
private String name;
|
||||
private RosterPacket.ItemType type;
|
||||
private RosterPacket.ItemStatus status;
|
||||
private final boolean approved;
|
||||
final private Roster roster;
|
||||
final private XMPPConnection connection;
|
||||
|
||||
|
@ -57,14 +58,16 @@ public class RosterEntry {
|
|||
* @param name the nickname for the entry.
|
||||
* @param type the subscription type.
|
||||
* @param status the subscription status (related to subscriptions pending to be approbed).
|
||||
* @param approved the pre-approval flag.
|
||||
* @param connection a connection to the XMPP server.
|
||||
*/
|
||||
RosterEntry(Jid user, String name, RosterPacket.ItemType type,
|
||||
RosterPacket.ItemStatus status, Roster roster, XMPPConnection connection) {
|
||||
RosterPacket.ItemStatus status, boolean approved, Roster roster, XMPPConnection connection) {
|
||||
this.user = user;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.status = status;
|
||||
this.approved = approved;
|
||||
this.roster = roster;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
@ -124,6 +127,15 @@ public class RosterEntry {
|
|||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pre-approval state of this entry.
|
||||
*
|
||||
* @return the pre-approval state.
|
||||
*/
|
||||
public boolean isApproved() {
|
||||
return approved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an copied list of the roster groups that this entry belongs to.
|
||||
*
|
||||
|
@ -244,6 +256,8 @@ public class RosterEntry {
|
|||
}
|
||||
else if (!user.equals(other.user))
|
||||
return false;
|
||||
if (approved != other.approved)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -251,6 +265,7 @@ public class RosterEntry {
|
|||
RosterPacket.Item item = new RosterPacket.Item(entry.getUser(), entry.getName());
|
||||
item.setItemType(entry.getType());
|
||||
item.setItemStatus(entry.getStatus());
|
||||
item.setApproved(entry.isApproved());
|
||||
// Set the correct group names for the item.
|
||||
for (RosterGroup group : entry.getGroups()) {
|
||||
item.addGroupName(group.getName());
|
||||
|
|
|
@ -111,6 +111,7 @@ public class RosterPacket extends IQ {
|
|||
private String name;
|
||||
private ItemType itemType;
|
||||
private ItemStatus itemStatus;
|
||||
private boolean approved;
|
||||
private final Set<String> groupNames;
|
||||
|
||||
/**
|
||||
|
@ -190,6 +191,25 @@ public class RosterPacket extends IQ {
|
|||
this.itemStatus = itemStatus;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the roster item pre-approval state.
|
||||
*
|
||||
* @return the pre-approval state.
|
||||
*/
|
||||
public boolean isApproved() {
|
||||
return approved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the roster item pre-approval state.
|
||||
*
|
||||
* @param approved the pre-approval flag.
|
||||
*/
|
||||
public void setApproved(boolean approved) {
|
||||
this.approved = approved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable set of the group names that the roster item
|
||||
* belongs to.
|
||||
|
@ -224,6 +244,7 @@ public class RosterPacket extends IQ {
|
|||
xml.optAttribute("name", name);
|
||||
xml.optAttribute("subscription", itemType);
|
||||
xml.optAttribute("ask", itemStatus);
|
||||
xml.optBooleanAttribute("approved", approved);
|
||||
xml.rightAngleBracket();
|
||||
|
||||
for (String groupName : groupNames) {
|
||||
|
@ -242,6 +263,7 @@ public class RosterPacket extends IQ {
|
|||
result = prime * result + ((itemType == null) ? 0 : itemType.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((user == null) ? 0 : user.hashCode());
|
||||
result = prime * result + ((approved == false) ? 0 : 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -276,6 +298,8 @@ public class RosterPacket extends IQ {
|
|||
}
|
||||
else if (!user.equals(other.user))
|
||||
return false;
|
||||
if (approved != other.approved)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2015 Tomáš Havlas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.roster.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class SubscriptionPreApproval implements ExtensionElement {
|
||||
|
||||
public static final String ELEMENT = "sub";
|
||||
public static final String NAMESPACE = "urn:xmpp:features:pre-approval";
|
||||
|
||||
public static final SubscriptionPreApproval INSTANCE = new SubscriptionPreApproval();
|
||||
|
||||
private SubscriptionPreApproval() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.closeEmptyElement();
|
||||
return xml;
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.provider.IQProvider;
|
||||
import org.jivesoftware.smack.roster.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
@ -59,6 +60,9 @@ public class RosterPacketProvider extends IQProvider<RosterPacket> {
|
|||
String subscription = parser.getAttributeValue("", "subscription");
|
||||
RosterPacket.ItemType type = RosterPacket.ItemType.valueOf(subscription != null ? subscription : "none");
|
||||
item.setItemType(type);
|
||||
// Set approval status.
|
||||
boolean approved = ParserUtils.getBooleanAttribute(parser, "approved", false);
|
||||
item.setApproved(approved);
|
||||
break;
|
||||
case "group":
|
||||
// TODO item!= null
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2015 Tomáš Havlas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.roster.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||
import org.jivesoftware.smack.roster.packet.SubscriptionPreApproval;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
public class SubscriptionPreApprovalStreamFeatureProvider extends ExtensionElementProvider<SubscriptionPreApproval> {
|
||||
|
||||
@Override
|
||||
public SubscriptionPreApproval parse(XmlPullParser parser, int initialDepth)
|
||||
throws XmlPullParserException, IOException, SmackException {
|
||||
return SubscriptionPreApproval.INSTANCE;
|
||||
}
|
||||
|
||||
}
|
|
@ -188,6 +188,7 @@ public class DirectoryRosterStore implements RosterStore {
|
|||
String name = null;
|
||||
String type = null;
|
||||
String status = null;
|
||||
String approved = null;
|
||||
|
||||
List<String> groupNames = new ArrayList<String>();
|
||||
|
||||
|
@ -220,6 +221,10 @@ public class DirectoryRosterStore implements RosterStore {
|
|||
parser.next();
|
||||
status = parser.getText();
|
||||
}
|
||||
else if (parserName.equals("approved")) {
|
||||
parser.next();
|
||||
approved = parser.getText();
|
||||
}
|
||||
else if (parserName.equals("group")) {
|
||||
parser.next();
|
||||
parser.next();
|
||||
|
@ -275,6 +280,9 @@ public class DirectoryRosterStore implements RosterStore {
|
|||
item.setItemStatus(itemStatus);
|
||||
}
|
||||
}
|
||||
if (approved != null) {
|
||||
item.setApproved(Boolean.parseBoolean(approved));
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
@ -287,6 +295,7 @@ public class DirectoryRosterStore implements RosterStore {
|
|||
xml.optElement("name", item.getName());
|
||||
xml.optElement("type", item.getItemType());
|
||||
xml.optElement("status", item.getItemStatus());
|
||||
xml.optElement("approved", Boolean.toString(item.isApproved()));
|
||||
for (String groupName : item.getGroupNames()) {
|
||||
xml.openElement("group");
|
||||
xml.element("groupName", groupName);
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
<className>org.jivesoftware.smack.roster.provider.RosterPacketProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<streamFeatureProvider>
|
||||
<elementName>sub</elementName>
|
||||
<namespace>urn:xmpp:features:pre-approval</namespace>
|
||||
<className>org.jivesoftware.smack.roster.provider.SubscriptionPreApprovalStreamFeatureProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<streamFeatureProvider>
|
||||
<elementName>ver</elementName>
|
||||
<namespace>urn:xmpp:features:rosterver</namespace>
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2015 Tomáš Havlas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.roster;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.jivesoftware.smack.DummyConnection;
|
||||
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
||||
import org.jivesoftware.smack.im.InitSmackIm;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.IQ.Type;
|
||||
import org.jivesoftware.smack.roster.RosterTest.TestRosterListener;
|
||||
import org.jivesoftware.smack.roster.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.roster.packet.RosterPacket.Item;
|
||||
import org.jivesoftware.smack.roster.packet.RosterPacket.ItemType;
|
||||
import org.jivesoftware.smack.roster.packet.SubscriptionPreApproval;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
|
||||
/**
|
||||
* Tests that verifies the correct behavior of the pre-approval implementation.
|
||||
*
|
||||
* @see <a href="http://xmpp.org/rfcs/rfc6121.html#sub-preapproval">Pre-Approving a Subscription Request</a>
|
||||
* @author Tomáš Havlas
|
||||
*/
|
||||
public class SubscriptionPreApprovalTest extends InitSmackIm {
|
||||
|
||||
private DummyConnection connection;
|
||||
private Roster roster;
|
||||
private TestRosterListener rosterListener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
connection = new DummyConnection();
|
||||
connection.connect();
|
||||
connection.login();
|
||||
rosterListener = new TestRosterListener();
|
||||
roster = Roster.getInstanceFor(connection);
|
||||
roster.addRosterListener(rosterListener);
|
||||
connection.setPacketReplyTimeout(1000 * 60 * 5);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
connection.disconnect();
|
||||
connection = null;
|
||||
}
|
||||
|
||||
@Test(expected=FeatureNotSupportedException.class)
|
||||
public void testPreApprovalNotSupported() throws Throwable {
|
||||
final Jid contactJID = JidCreate.from("preapproval@example.com");
|
||||
roster.preApprove(contactJID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreApproveAndCreate() throws Throwable {
|
||||
final Jid contactJID = JidCreate.from("preapproval@example.com");
|
||||
final String contactName = "PreApproval";
|
||||
final String[] contactGroup = {};
|
||||
connection.enableStreamFeature(SubscriptionPreApproval.INSTANCE);
|
||||
|
||||
final PreApproveAndCreateEntryResponder serverSimulator = new PreApproveAndCreateEntryResponder() {
|
||||
@Override
|
||||
void verifyRosterUpdateRequest(final RosterPacket updateRequest) {
|
||||
final Item item = updateRequest.getRosterItems().iterator().next();
|
||||
assertSame("The provided JID doesn't match the requested!",
|
||||
contactJID,
|
||||
item.getUser());
|
||||
assertSame("The provided name doesn't match the requested!",
|
||||
contactName,
|
||||
item.getName());
|
||||
assertSame("The provided group number doesn't match the requested!",
|
||||
0,
|
||||
item.getGroupNames().size());
|
||||
}
|
||||
|
||||
@Override
|
||||
void verifyPreApprovalRequest(Presence preApproval) {
|
||||
assertSame("The provided name doesn't match the requested!",
|
||||
contactJID,
|
||||
preApproval.getTo());
|
||||
assertSame("The provided presence type is incorrect!",
|
||||
Presence.Type.subscribed,
|
||||
preApproval.getType());
|
||||
}
|
||||
};
|
||||
serverSimulator.start();
|
||||
roster.preApproveAndCreateEntry(contactJID, contactName, contactGroup);
|
||||
serverSimulator.join();
|
||||
|
||||
// Check if an error occurred within the simulator
|
||||
final Throwable exception = serverSimulator.getException();
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
rosterListener.waitUntilInvocationOrTimeout();
|
||||
|
||||
// Verify the roster entry of the new contact
|
||||
final RosterEntry addedEntry = roster.getEntry(contactJID);
|
||||
assertNotNull("The new contact wasn't added to the roster!", addedEntry);
|
||||
assertTrue("The roster listener wasn't invoked for the new contact!",
|
||||
rosterListener.getAddedAddresses().contains(contactJID));
|
||||
assertSame("Setup wrong name for the new contact!",
|
||||
contactName,
|
||||
addedEntry.getName());
|
||||
assertSame("Setup wrong default subscription status!",
|
||||
ItemType.none,
|
||||
addedEntry.getType());
|
||||
assertSame("The new contact should be member of exactly one group!",
|
||||
0,
|
||||
addedEntry.getGroups().size());
|
||||
}
|
||||
|
||||
/**
|
||||
* This class can be used to simulate the server response for
|
||||
* a pre approve request request.
|
||||
*/
|
||||
private abstract class PreApproveAndCreateEntryResponder extends Thread {
|
||||
private Throwable exception = null;
|
||||
|
||||
/**
|
||||
* Overwrite this method to check if the received roster update request is valid.
|
||||
*
|
||||
* @param updateRequest the request which would be sent to the server.
|
||||
*/
|
||||
abstract void verifyRosterUpdateRequest(final RosterPacket updateRequest);
|
||||
/**
|
||||
* Overwrite this method to check if recieved pre-approval request is valid
|
||||
*
|
||||
* @param preApproval the request which would be sent to server.
|
||||
*/
|
||||
abstract void verifyPreApprovalRequest(final Presence preApproval);
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
final Stanza packet = connection.getSentPacket();
|
||||
if (packet instanceof RosterPacket && ((IQ) packet).getType() == Type.set) {
|
||||
final RosterPacket rosterRequest = (RosterPacket) packet;
|
||||
|
||||
// Prepare and process the roster push
|
||||
final RosterPacket rosterPush = new RosterPacket();
|
||||
final Item item = rosterRequest.getRosterItems().iterator().next();
|
||||
if (item.getItemType() != ItemType.remove) {
|
||||
item.setItemType(ItemType.none);
|
||||
}
|
||||
rosterPush.setType(Type.set);
|
||||
rosterPush.setTo(connection.getUser());
|
||||
rosterPush.addRosterItem(item);
|
||||
connection.processPacket(rosterPush);
|
||||
|
||||
// Create and process the IQ response
|
||||
final IQ response = IQ.createResultIQ(rosterRequest);
|
||||
connection.processPacket(response);
|
||||
|
||||
// Verify the roster update request
|
||||
assertSame("A roster set MUST contain one and only one <item/> element.",
|
||||
1,
|
||||
rosterRequest.getRosterItemCount());
|
||||
verifyRosterUpdateRequest(rosterRequest);
|
||||
break;
|
||||
}
|
||||
else if (packet instanceof Presence && ((Presence) packet).getType() == Presence.Type.subscribed) {
|
||||
final Presence approval = (Presence) packet;
|
||||
verifyPreApprovalRequest(approval);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
exception = e;
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception or error if something went wrong.
|
||||
*
|
||||
* @return the Throwable exception or error that occurred.
|
||||
*/
|
||||
public Throwable getException() {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,6 +107,8 @@ public class DirectoryRosterStoreTest {
|
|||
item1.getItemType(), storedItem.getItemType());
|
||||
assertEquals("ItemStatus of added entry",
|
||||
item1.getItemStatus(), storedItem.getItemStatus());
|
||||
assertEquals("Approved of added entry",
|
||||
item1.isApproved(), storedItem.isApproved());
|
||||
|
||||
|
||||
final String version2 = "2";
|
||||
|
@ -115,6 +117,7 @@ public class DirectoryRosterStoreTest {
|
|||
item2.addGroupName("examples");
|
||||
item2.setItemStatus(ItemStatus.subscribe);
|
||||
item2.setItemType(ItemType.none);
|
||||
item2.setApproved(true);
|
||||
store.addEntry(item2,version2);
|
||||
assertEquals("Updating entry sets version correctly", version2, store.getRosterVersion());
|
||||
storedItem = store.getEntry(userName);
|
||||
|
@ -128,6 +131,8 @@ public class DirectoryRosterStoreTest {
|
|||
item2.getItemType(), storedItem.getItemType());
|
||||
assertEquals("ItemStatus of added entry",
|
||||
item2.getItemStatus(), storedItem.getItemStatus());
|
||||
assertEquals("Approved of added entry",
|
||||
item2.isApproved(), storedItem.isApproved());
|
||||
|
||||
List<Item> entries = store.getEntries();
|
||||
assertEquals("Number of entries", 1, entries.size());
|
||||
|
@ -143,6 +148,7 @@ public class DirectoryRosterStoreTest {
|
|||
item4.addGroupName("Bar Friends");
|
||||
item4.setItemStatus(ItemStatus.subscribe);
|
||||
item4.setItemType(ItemType.both);
|
||||
item4.setApproved(true);
|
||||
|
||||
ArrayList<Item> items34 = new ArrayList<RosterPacket.Item>();
|
||||
items34.add(item3);
|
||||
|
@ -162,6 +168,8 @@ public class DirectoryRosterStoreTest {
|
|||
item3.getItemType(), storedItem.getItemType());
|
||||
assertEquals("ItemStatus of added entry",
|
||||
item3.getItemStatus(), storedItem.getItemStatus());
|
||||
assertEquals("Approved of added entry",
|
||||
item3.isApproved(), storedItem.isApproved());
|
||||
|
||||
|
||||
storedItem = store.getEntry(JidTestUtil.BARE_JID_2);
|
||||
|
@ -175,6 +183,8 @@ public class DirectoryRosterStoreTest {
|
|||
item4.getItemType(), storedItem.getItemType());
|
||||
assertEquals("ItemStatus of added entry",
|
||||
item4.getItemStatus(), storedItem.getItemStatus());
|
||||
assertEquals("Approved of added entry",
|
||||
item4.isApproved(), storedItem.isApproved());
|
||||
|
||||
entries = store.getEntries();
|
||||
assertEquals("Number of entries", 2, entries.size());
|
||||
|
|
Loading…
Reference in a new issue