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:
parent
f410ece468
commit
fae9d129a9
12 changed files with 440 additions and 5 deletions
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue