mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-26 12:37:58 +01:00
Improve Privacy List code
notably add a cache for the active and default privacy list to avoid IQ get/response round-trips. Also add a few methods to PrivacyListManager to get the privacy list names. The already existing methods always returned the whole list together with the name, which caused two round-trips. Simplified some code. Properly escape Privacy XML.
This commit is contained in:
parent
67c0a7089b
commit
3dd1365a5a
8 changed files with 299 additions and 18 deletions
|
@ -1371,6 +1371,27 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
sendStanzaWithResponseCallback(iqRequest, replyFilter, callback, exceptionCallback, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOneTimeSyncCallback(final PacketListener callback, final PacketFilter packetFilter) {
|
||||
final PacketListener packetListener = new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) throws NotConnectedException {
|
||||
try {
|
||||
callback.processPacket(packet);
|
||||
} finally {
|
||||
removeSyncPacketListener(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
addSyncPacketListener(packetListener, packetFilter);
|
||||
removeCallbacksService.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
removeSyncPacketListener(packetListener);
|
||||
}
|
||||
}, getPacketReplyTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private long lastStanzaReceived;
|
||||
|
||||
public long getLastStanzaReceived() {
|
||||
|
|
|
@ -556,6 +556,15 @@ public interface XMPPConnection {
|
|||
final ExceptionCallback exceptionCallback, long timeout)
|
||||
throws NotConnectedException;
|
||||
|
||||
/**
|
||||
* Add a callback that is called exactly once and synchronously with the incoming stanza that matches the given
|
||||
* packet filter.
|
||||
*
|
||||
* @param callback the callback invoked once the packet filter matches a stanza.
|
||||
* @param packetFilter the filter to match stanzas or null to match all.
|
||||
*/
|
||||
public void addOneTimeSyncCallback(PacketListener callback, PacketFilter packetFilter);
|
||||
|
||||
/**
|
||||
* Returns the timestamp in milliseconds when the last stanza was received.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015 Florian Schmaus
|
||||
*
|
||||
* 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.filter;
|
||||
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IQResultReplyFilter extends IQReplyFilter {
|
||||
|
||||
|
||||
public IQResultReplyFilter(IQ iqPacket, XMPPConnection conn) {
|
||||
super(iqPacket, conn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Packet packet) {
|
||||
if (!super.accept(packet)) {
|
||||
return false;
|
||||
}
|
||||
return IQTypeFilter.RESULT.accept(packet);
|
||||
}
|
||||
|
||||
}
|
|
@ -70,4 +70,8 @@ public class PrivacyList {
|
|||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Privacy List: " + listName + "(active:" + isActiveList + ", default:" + isDefaultList + ")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
|||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
|
@ -32,25 +33,29 @@ import org.jivesoftware.smack.PacketListener;
|
|||
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.filter.AndFilter;
|
||||
import org.jivesoftware.smack.filter.IQResultReplyFilter;
|
||||
import org.jivesoftware.smack.filter.IQTypeFilter;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.privacy.filter.SetActiveListFilter;
|
||||
import org.jivesoftware.smackx.privacy.filter.SetDefaultListFilter;
|
||||
import org.jivesoftware.smackx.privacy.packet.Privacy;
|
||||
import org.jivesoftware.smackx.privacy.packet.PrivacyItem;
|
||||
|
||||
/**
|
||||
* A PrivacyListManager is used by XMPP clients to block or allow communications from other
|
||||
* users. Use the manager to: <ul>
|
||||
* users. Use the manager to:
|
||||
* <ul>
|
||||
* <li>Retrieve privacy lists.
|
||||
* <li>Add, remove, and edit privacy lists.
|
||||
* <li>Set, change, or decline active lists.
|
||||
* <li>Set, change, or decline the default list (i.e., the list that is active by default).
|
||||
* </ul>
|
||||
* Privacy Items can handle different kind of permission communications based on JID, group,
|
||||
* subscription type or globally (@see PrivacyItem).
|
||||
* subscription type or globally (see {@link PrivacyItem}).
|
||||
*
|
||||
* @author Francisco Vives
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0016.html">XEP-16: Privacy Lists</a>
|
||||
|
@ -58,8 +63,11 @@ import org.jivesoftware.smackx.privacy.packet.PrivacyItem;
|
|||
public class PrivacyListManager extends Manager {
|
||||
public static final String NAMESPACE = Privacy.NAMESPACE;
|
||||
|
||||
private static final PacketFilter PACKET_FILTER = new AndFilter(IQTypeFilter.SET,
|
||||
new PacketTypeFilter(Privacy.class));
|
||||
public static final PacketFilter PRIVACY_FILTER = new PacketTypeFilter(Privacy.class);
|
||||
|
||||
private static final PacketFilter PRIVACY_SET = new AndFilter(IQTypeFilter.SET, PRIVACY_FILTER);
|
||||
|
||||
private static final PacketFilter PRIVACY_RESULT = new AndFilter(IQTypeFilter.RESULT, PRIVACY_FILTER);
|
||||
|
||||
// Keep the list of instances of this class.
|
||||
private static final Map<XMPPConnection, PrivacyListManager> INSTANCES = new WeakHashMap<XMPPConnection, PrivacyListManager>();
|
||||
|
@ -75,6 +83,10 @@ public class PrivacyListManager extends Manager {
|
|||
});
|
||||
}
|
||||
|
||||
// TODO implement: private final Map<String, PrivacyList> cachedPrivacyLists = new HashMap<>();
|
||||
private volatile String cachedActiveListName;
|
||||
private volatile String cachedDefaultListName;
|
||||
|
||||
/**
|
||||
* Creates a new privacy manager to maintain the communication privacy. Note: no
|
||||
* information is sent to or received from the server until you attempt to
|
||||
|
@ -82,7 +94,7 @@ public class PrivacyListManager extends Manager {
|
|||
*
|
||||
* @param connection the XMPP connection.
|
||||
*/
|
||||
private PrivacyListManager(final XMPPConnection connection) {
|
||||
private PrivacyListManager(XMPPConnection connection) {
|
||||
super(connection);
|
||||
|
||||
connection.addSyncPacketListener(new PacketListener() {
|
||||
|
@ -109,7 +121,78 @@ public class PrivacyListManager extends Manager {
|
|||
IQ iq = IQ.createResultIQ(privacy);
|
||||
connection().sendPacket(iq);
|
||||
}
|
||||
}, PACKET_FILTER);
|
||||
}, PRIVACY_SET);
|
||||
|
||||
// cached(Active|Default)ListName handling
|
||||
connection.addPacketSendingListener(new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) throws NotConnectedException {
|
||||
XMPPConnection connection = connection();
|
||||
Privacy privacy = (Privacy) packet;
|
||||
PacketFilter iqResultReplyFilter = new IQResultReplyFilter(privacy, connection);
|
||||
final String activeListName = privacy.getActiveName();
|
||||
final boolean declinceActiveList = privacy.isDeclineActiveList();
|
||||
connection.addOneTimeSyncCallback(new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) throws NotConnectedException {
|
||||
if (declinceActiveList) {
|
||||
cachedActiveListName = null;
|
||||
}
|
||||
else {
|
||||
cachedActiveListName = activeListName;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, iqResultReplyFilter);
|
||||
}
|
||||
}, SetActiveListFilter.INSTANCE);
|
||||
connection.addPacketSendingListener(new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) throws NotConnectedException {
|
||||
XMPPConnection connection = connection();
|
||||
Privacy privacy = (Privacy) packet;
|
||||
PacketFilter iqResultReplyFilter = new IQResultReplyFilter(privacy, connection);
|
||||
final String defaultListName = privacy.getDefaultName();
|
||||
final boolean declinceDefaultList = privacy.isDeclineDefaultList();
|
||||
connection.addOneTimeSyncCallback(new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) throws NotConnectedException {
|
||||
if (declinceDefaultList) {
|
||||
cachedDefaultListName = null;
|
||||
}
|
||||
else {
|
||||
cachedDefaultListName = defaultListName;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, iqResultReplyFilter);
|
||||
}
|
||||
}, SetDefaultListFilter.INSTANCE);
|
||||
connection.addSyncPacketListener(new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) throws NotConnectedException {
|
||||
Privacy privacy = (Privacy) packet;
|
||||
// If a privacy IQ result stanza has an active or default list name set, then we use that
|
||||
// as cached list name.
|
||||
String activeList = privacy.getActiveName();
|
||||
if (activeList != null) {
|
||||
cachedActiveListName = activeList;
|
||||
}
|
||||
String defaultList = privacy.getDefaultName();
|
||||
if (defaultList != null) {
|
||||
cachedDefaultListName = defaultList;
|
||||
}
|
||||
}
|
||||
}, PRIVACY_RESULT);
|
||||
connection.addConnectionListener(new AbstractConnectionListener() {
|
||||
@Override
|
||||
public void reconnectionSuccessful() {
|
||||
cachedActiveListName = cachedDefaultListName = null;
|
||||
}
|
||||
});
|
||||
|
||||
// XEP-0016 § 3.
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(NAMESPACE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,13 +274,26 @@ public class PrivacyListManager extends Manager {
|
|||
public PrivacyList getActiveList() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||
Privacy privacyAnswer = this.getPrivacyWithListNames();
|
||||
String listName = privacyAnswer.getActiveName();
|
||||
boolean isDefaultAndActive = privacyAnswer.getActiveName() != null
|
||||
&& privacyAnswer.getDefaultName() != null
|
||||
&& privacyAnswer.getActiveName().equals(
|
||||
privacyAnswer.getDefaultName());
|
||||
boolean isDefaultAndActive = listName != null && listName.equals(privacyAnswer.getDefaultName());
|
||||
return new PrivacyList(true, isDefaultAndActive, listName, getPrivacyListItems(listName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the active list.
|
||||
*
|
||||
* @return the name of the active list or null if there is none set.
|
||||
* @throws NoResponseException
|
||||
* @throws XMPPErrorException
|
||||
* @throws NotConnectedException
|
||||
* @since 4.1
|
||||
*/
|
||||
public String getActiveListName() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||
if (cachedActiveListName != null) {
|
||||
return cachedActiveListName;
|
||||
}
|
||||
return getPrivacyWithListNames().getActiveName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer the default privacy list.
|
||||
*
|
||||
|
@ -209,13 +305,47 @@ public class PrivacyListManager extends Manager {
|
|||
public PrivacyList getDefaultList() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||
Privacy privacyAnswer = this.getPrivacyWithListNames();
|
||||
String listName = privacyAnswer.getDefaultName();
|
||||
boolean isDefaultAndActive = privacyAnswer.getActiveName() != null
|
||||
&& privacyAnswer.getDefaultName() != null
|
||||
&& privacyAnswer.getActiveName().equals(
|
||||
privacyAnswer.getDefaultName());
|
||||
boolean isDefaultAndActive = listName != null && listName.equals(privacyAnswer.getActiveName());
|
||||
return new PrivacyList(isDefaultAndActive, true, listName, getPrivacyListItems(listName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the default list.
|
||||
*
|
||||
* @return the name of the default list or null if there is none set.
|
||||
* @throws NoResponseException
|
||||
* @throws XMPPErrorException
|
||||
* @throws NotConnectedException
|
||||
* @since 4.1
|
||||
*/
|
||||
public String getDefaultListName() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||
if (cachedDefaultListName != null) {
|
||||
return cachedDefaultListName;
|
||||
}
|
||||
return getPrivacyWithListNames().getDefaultName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the effective privacy list.
|
||||
* <p>
|
||||
* The effective privacy list is the one that is currently enforced on the connection. It's either the active
|
||||
* privacy list, or, if the active privacy list is not set, the default privacy list.
|
||||
* </p>
|
||||
*
|
||||
* @return the name of the effective privacy list or null if there is none set.
|
||||
* @throws NoResponseException
|
||||
* @throws XMPPErrorException
|
||||
* @throws NotConnectedException
|
||||
* @since 4.1
|
||||
*/
|
||||
public String getEffectiveListName() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||
String activeListName = getActiveListName();
|
||||
if (activeListName != null) {
|
||||
return activeListName;
|
||||
}
|
||||
return getDefaultListName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer the privacy list items under listName with the allowed and blocked permissions.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015 Florian Schmaus
|
||||
*
|
||||
* 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.smackx.privacy.filter;
|
||||
|
||||
import org.jivesoftware.smack.filter.FlexiblePacketTypeFilter;
|
||||
import org.jivesoftware.smack.packet.IQ.Type;
|
||||
import org.jivesoftware.smackx.privacy.packet.Privacy;
|
||||
|
||||
public class SetActiveListFilter extends FlexiblePacketTypeFilter<Privacy> {
|
||||
|
||||
public static final SetActiveListFilter INSTANCE = new SetActiveListFilter();
|
||||
|
||||
private SetActiveListFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean acceptSpecific(Privacy privacy) {
|
||||
if (privacy.getType() != Type.set) {
|
||||
return false;
|
||||
}
|
||||
return privacy.getActiveName() != null || privacy.isDeclineActiveList();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015 Florian Schmaus
|
||||
*
|
||||
* 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.smackx.privacy.filter;
|
||||
|
||||
import org.jivesoftware.smack.filter.FlexiblePacketTypeFilter;
|
||||
import org.jivesoftware.smack.packet.IQ.Type;
|
||||
import org.jivesoftware.smackx.privacy.packet.Privacy;
|
||||
|
||||
public class SetDefaultListFilter extends FlexiblePacketTypeFilter<Privacy> {
|
||||
|
||||
public static final SetDefaultListFilter INSTANCE = new SetDefaultListFilter();
|
||||
|
||||
private SetDefaultListFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean acceptSpecific(Privacy privacy) {
|
||||
if (privacy.getType() != Type.set) {
|
||||
return false;
|
||||
}
|
||||
return privacy.getDefaultName() != null || privacy.isDeclineDefaultList();
|
||||
}
|
||||
|
||||
}
|
|
@ -292,7 +292,7 @@ public class Privacy extends IQ {
|
|||
buf.append("<active/>");
|
||||
} else {
|
||||
if (this.getActiveName() != null) {
|
||||
buf.append("<active name=\"").append(this.getActiveName()).append("\"/>");
|
||||
buf.append("<active name=\"").escape(getActiveName()).append("\"/>");
|
||||
}
|
||||
}
|
||||
// Add the default tag
|
||||
|
@ -300,7 +300,7 @@ public class Privacy extends IQ {
|
|||
buf.append("<default/>");
|
||||
} else {
|
||||
if (this.getDefaultName() != null) {
|
||||
buf.append("<default name=\"").append(this.getDefaultName()).append("\"/>");
|
||||
buf.append("<default name=\"").escape(getDefaultName()).append("\"/>");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,9 +310,9 @@ public class Privacy extends IQ {
|
|||
List<PrivacyItem> items = entry.getValue();
|
||||
// Begin the list tag
|
||||
if (items.isEmpty()) {
|
||||
buf.append("<list name=\"").append(listName).append("\"/>");
|
||||
buf.append("<list name=\"").escape(listName).append("\"/>");
|
||||
} else {
|
||||
buf.append("<list name=\"").append(listName).append("\">");
|
||||
buf.append("<list name=\"").escape(listName).append("\">");
|
||||
}
|
||||
for (PrivacyItem item : items) {
|
||||
// Append the item xml representation
|
||||
|
|
Loading…
Reference in a new issue