/** * $Revision$ * $Date$ * * Copyright 2006-2007 Jive Software. * * All rights reserved. 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; import org.jivesoftware.smack.filter.*; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Privacy; import org.jivesoftware.smack.packet.PrivacyItem; import java.util.*; /** * A PrivacyListManager is used by XMPP clients to block or allow communications from other * 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). * * @author Francisco Vives */ public class PrivacyListManager { // Keep the list of instances of this class. private static Map<Connection, PrivacyListManager> instances = new Hashtable<Connection, PrivacyListManager>(); private Connection connection; private final List<PrivacyListListener> listeners = new ArrayList<PrivacyListListener>(); PacketFilter packetFilter = new AndFilter(new IQTypeFilter(IQ.Type.SET), new PacketExtensionFilter("query", "jabber:iq:privacy")); static { // Create a new PrivacyListManager on every established connection. In the init() // method of PrivacyListManager, we'll add a listener that will delete the // instance when the connection is closed. Connection.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(Connection connection) { new PrivacyListManager(connection); } }); } /** * 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 * get or set the privacy communication.<p> * * @param connection the XMPP connection. */ private PrivacyListManager(Connection connection) { this.connection = connection; this.init(); } /** Answer the connection userJID that owns the privacy. * @return the userJID that owns the privacy */ private String getUser() { return connection.getUser(); } /** * Initializes the packet listeners of the connection that will notify for any set privacy * package. */ private void init() { // Register the new instance and associate it with the connection instances.put(connection, this); // Add a listener to the connection that removes the registered instance when // the connection is closed connection.addConnectionListener(new ConnectionListener() { public void connectionClosed() { // Unregister this instance since the connection has been closed instances.remove(connection); } public void connectionClosedOnError(Exception e) { // ignore } public void reconnectionFailed(Exception e) { // ignore } public void reconnectingIn(int seconds) { // ignore } public void reconnectionSuccessful() { // ignore } }); connection.addPacketListener(new PacketListener() { public void processPacket(Packet packet) { if (packet == null || packet.getError() != null) { return; } // The packet is correct. Privacy privacy = (Privacy) packet; // Notifies the event to the listeners. synchronized (listeners) { for (PrivacyListListener listener : listeners) { // Notifies the created or updated privacy lists for (Map.Entry<String,List<PrivacyItem>> entry : privacy.getItemLists().entrySet()) { String listName = entry.getKey(); List<PrivacyItem> items = entry.getValue(); if (items.isEmpty()) { listener.updatedPrivacyList(listName); } else { listener.setPrivacyList(listName, items); } } } } // Send a result package acknowledging the reception of a privacy package. // Prepare the IQ packet to send IQ iq = new IQ() { public String getChildElementXML() { return ""; } }; iq.setType(IQ.Type.RESULT); iq.setFrom(packet.getFrom()); iq.setPacketID(packet.getPacketID()); // Send create & join packet. connection.sendPacket(iq); } }, packetFilter); } /** * Returns the PrivacyListManager instance associated with a given Connection. * * @param connection the connection used to look for the proper PrivacyListManager. * @return the PrivacyListManager associated with a given Connection. */ public static PrivacyListManager getInstanceFor(Connection connection) { return instances.get(connection); } /** * Send the {@link Privacy} packet to the server in order to know some privacy content and then * waits for the answer. * * @param requestPrivacy is the {@link Privacy} packet configured properly whose XML * will be sent to the server. * @return a new {@link Privacy} with the data received from the server. * @exception XMPPException if the request or the answer failed, it raises an exception. */ private Privacy getRequest(Privacy requestPrivacy) throws XMPPException { // The request is a get iq type requestPrivacy.setType(Privacy.Type.GET); requestPrivacy.setFrom(this.getUser()); // Filter packets looking for an answer from the server. PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID()); PacketCollector response = connection.createPacketCollector(responseFilter); // Send create & join packet. connection.sendPacket(requestPrivacy); // Wait up to a certain number of seconds for a reply. Privacy privacyAnswer = (Privacy) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); // Stop queuing results response.cancel(); // Interprete the result and answer the privacy only if it is valid if (privacyAnswer == null) { throw new XMPPException("No response from server."); } else if (privacyAnswer.getError() != null) { throw new XMPPException(privacyAnswer.getError()); } return privacyAnswer; } /** * Send the {@link Privacy} packet to the server in order to modify the server privacy and * waits for the answer. * * @param requestPrivacy is the {@link Privacy} packet configured properly whose xml will be sent * to the server. * @return a new {@link Privacy} with the data received from the server. * @exception XMPPException if the request or the answer failed, it raises an exception. */ private Packet setRequest(Privacy requestPrivacy) throws XMPPException { // The request is a get iq type requestPrivacy.setType(Privacy.Type.SET); requestPrivacy.setFrom(this.getUser()); // Filter packets looking for an answer from the server. PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID()); PacketCollector response = connection.createPacketCollector(responseFilter); // Send create & join packet. connection.sendPacket(requestPrivacy); // Wait up to a certain number of seconds for a reply. Packet privacyAnswer = response.nextResult(SmackConfiguration.getPacketReplyTimeout()); // Stop queuing results response.cancel(); // Interprete the result and answer the privacy only if it is valid if (privacyAnswer == null) { throw new XMPPException("No response from server."); } else if (privacyAnswer.getError() != null) { throw new XMPPException(privacyAnswer.getError()); } return privacyAnswer; } /** * Answer a privacy containing the list structre without {@link PrivacyItem}. * * @return a Privacy with the list names. * @throws XMPPException if an error occurs. */ private Privacy getPrivacyWithListNames() throws XMPPException { // The request of the list is an empty privacy message Privacy request = new Privacy(); // Send the package to the server and get the answer return getRequest(request); } /** * Answer the active privacy list. * * @return the privacy list of the active list. * @throws XMPPException if an error occurs. */ public PrivacyList getActiveList() throws XMPPException { Privacy privacyAnswer = this.getPrivacyWithListNames(); String listName = privacyAnswer.getActiveName(); boolean isDefaultAndActive = privacyAnswer.getActiveName() != null && privacyAnswer.getDefaultName() != null && privacyAnswer.getActiveName().equals( privacyAnswer.getDefaultName()); return new PrivacyList(true, isDefaultAndActive, listName, getPrivacyListItems(listName)); } /** * Answer the default privacy list. * * @return the privacy list of the default list. * @throws XMPPException if an error occurs. */ public PrivacyList getDefaultList() throws XMPPException { Privacy privacyAnswer = this.getPrivacyWithListNames(); String listName = privacyAnswer.getDefaultName(); boolean isDefaultAndActive = privacyAnswer.getActiveName() != null && privacyAnswer.getDefaultName() != null && privacyAnswer.getActiveName().equals( privacyAnswer.getDefaultName()); return new PrivacyList(isDefaultAndActive, true, listName, getPrivacyListItems(listName)); } /** * Answer the privacy list items under listName with the allowed and blocked permissions. * * @param listName the name of the list to get the allowed and blocked permissions. * @return a list of privacy items under the list listName. * @throws XMPPException if an error occurs. */ private List<PrivacyItem> getPrivacyListItems(String listName) throws XMPPException { // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setPrivacyList(listName, new ArrayList<PrivacyItem>()); // Send the package to the server and get the answer Privacy privacyAnswer = getRequest(request); return privacyAnswer.getPrivacyList(listName); } /** * Answer the privacy list items under listName with the allowed and blocked permissions. * * @param listName the name of the list to get the allowed and blocked permissions. * @return a privacy list under the list listName. * @throws XMPPException if an error occurs. */ public PrivacyList getPrivacyList(String listName) throws XMPPException { return new PrivacyList(false, false, listName, getPrivacyListItems(listName)); } /** * Answer every privacy list with the allowed and blocked permissions. * * @return an array of privacy lists. * @throws XMPPException if an error occurs. */ public PrivacyList[] getPrivacyLists() throws XMPPException { Privacy privacyAnswer = this.getPrivacyWithListNames(); Set<String> names = privacyAnswer.getPrivacyListNames(); PrivacyList[] lists = new PrivacyList[names.size()]; boolean isActiveList; boolean isDefaultList; int index=0; for (String listName : names) { isActiveList = listName.equals(privacyAnswer.getActiveName()); isDefaultList = listName.equals(privacyAnswer.getDefaultName()); lists[index] = new PrivacyList(isActiveList, isDefaultList, listName, getPrivacyListItems(listName)); index = index + 1; } return lists; } /** * Set or change the active list to listName. * * @param listName the list name to set as the active one. * @exception XMPPException if the request or the answer failed, it raises an exception. */ public void setActiveListName(String listName) throws XMPPException { // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setActiveName(listName); // Send the package to the server setRequest(request); } /** * Client declines the use of active lists. * * @throws XMPPException if an error occurs. */ public void declineActiveList() throws XMPPException { // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setDeclineActiveList(true); // Send the package to the server setRequest(request); } /** * Set or change the default list to listName. * * @param listName the list name to set as the default one. * @exception XMPPException if the request or the answer failed, it raises an exception. */ public void setDefaultListName(String listName) throws XMPPException { // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setDefaultName(listName); // Send the package to the server setRequest(request); } /** * Client declines the use of default lists. * * @throws XMPPException if an error occurs. */ public void declineDefaultList() throws XMPPException { // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setDeclineDefaultList(true); // Send the package to the server setRequest(request); } /** * The client has created a new list. It send the new one to the server. * * @param listName the list that has changed its content. * @param privacyItems a List with every privacy item in the list. * @throws XMPPException if an error occurs. */ public void createPrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException { this.updatePrivacyList(listName, privacyItems); } /** * The client has edited an existing list. It updates the server content with the resulting * list of privacy items. The {@link PrivacyItem} list MUST contain all elements in the * list (not the "delta"). * * @param listName the list that has changed its content. * @param privacyItems a List with every privacy item in the list. * @throws XMPPException if an error occurs. */ public void updatePrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException { // Build the privacy package to add or update the new list Privacy request = new Privacy(); request.setPrivacyList(listName, privacyItems); // Send the package to the server setRequest(request); } /** * Remove a privacy list. * * @param listName the list that has changed its content. * @throws XMPPException if an error occurs. */ public void deletePrivacyList(String listName) throws XMPPException { // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setPrivacyList(listName, new ArrayList<PrivacyItem>()); // Send the package to the server setRequest(request); } /** * Adds a packet listener that will be notified of any new update in the user * privacy communication. * * @param listener a packet listener. */ public void addListener(PrivacyListListener listener) { // Keep track of the listener so that we can manually deliver extra // messages to it later if needed. synchronized (listeners) { listeners.add(listener); } } }