1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-12-08 14:11:07 +01:00

Added support for pre-approved subscription requests (RFC 6121 § 3.4)

SMACK-639
This commit is contained in:
Tomáš Havlas 2015-03-14 15:19:52 +01:00 committed by Florian Schmaus
parent f410ece468
commit fae9d129a9
12 changed files with 440 additions and 5 deletions

View file

@ -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)) {

View file

@ -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());

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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>