001/** 002 * 003 * Copyright 2016-2017 Fernando Ramirez, Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.blocking; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import java.util.WeakHashMap; 025import java.util.concurrent.CopyOnWriteArraySet; 026 027import org.jivesoftware.smack.AbstractConnectionListener; 028import org.jivesoftware.smack.ConnectionCreationListener; 029import org.jivesoftware.smack.Manager; 030import org.jivesoftware.smack.SmackException.NoResponseException; 031import org.jivesoftware.smack.SmackException.NotConnectedException; 032import org.jivesoftware.smack.XMPPConnection; 033import org.jivesoftware.smack.XMPPConnectionRegistry; 034import org.jivesoftware.smack.XMPPException.XMPPErrorException; 035import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; 036import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; 037import org.jivesoftware.smack.packet.IQ; 038import org.jivesoftware.smack.packet.IQ.Type; 039import org.jivesoftware.smackx.blocking.element.BlockContactsIQ; 040import org.jivesoftware.smackx.blocking.element.BlockListIQ; 041import org.jivesoftware.smackx.blocking.element.UnblockContactsIQ; 042import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 043import org.jxmpp.jid.Jid; 044 045/** 046 * Blocking command manager class. 047 * 048 * @author Fernando Ramirez 049 * @author Florian Schmaus 050 * @see <a href="http://xmpp.org/extensions/xep-0191.html">XEP-0191: Blocking 051 * Command</a> 052 */ 053public final class BlockingCommandManager extends Manager { 054 055 public static final String NAMESPACE = "urn:xmpp:blocking"; 056 057 private volatile List<Jid> blockListCached; 058 059 static { 060 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 061 @Override 062 public void connectionCreated(XMPPConnection connection) { 063 getInstanceFor(connection); 064 } 065 }); 066 } 067 068 private static final Map<XMPPConnection, BlockingCommandManager> INSTANCES = new WeakHashMap<>(); 069 070 /** 071 * Get the singleton instance of BlockingCommandManager. 072 * 073 * @param connection 074 * @return the instance of BlockingCommandManager 075 */ 076 public static synchronized BlockingCommandManager getInstanceFor(XMPPConnection connection) { 077 BlockingCommandManager blockingCommandManager = INSTANCES.get(connection); 078 079 if (blockingCommandManager == null) { 080 blockingCommandManager = new BlockingCommandManager(connection); 081 INSTANCES.put(connection, blockingCommandManager); 082 } 083 084 return blockingCommandManager; 085 } 086 087 private final Set<AllJidsUnblockedListener> allJidsUnblockedListeners = new CopyOnWriteArraySet<>(); 088 089 private final Set<JidsBlockedListener> jidsBlockedListeners = new CopyOnWriteArraySet<>(); 090 091 private final Set<JidsUnblockedListener> jidsUnblockedListeners = new CopyOnWriteArraySet<>(); 092 093 private BlockingCommandManager(XMPPConnection connection) { 094 super(connection); 095 096 // block IQ handler 097 connection.registerIQRequestHandler( 098 new AbstractIqRequestHandler(BlockContactsIQ.ELEMENT, BlockContactsIQ.NAMESPACE, Type.set, Mode.sync) { 099 @Override 100 public IQ handleIQRequest(IQ iqRequest) { 101 BlockContactsIQ blockContactIQ = (BlockContactsIQ) iqRequest; 102 103 if (blockListCached == null) { 104 blockListCached = new ArrayList<Jid>(); 105 } 106 107 List<Jid> blockedJids = blockContactIQ.getJids(); 108 blockListCached.addAll(blockedJids); 109 110 for (JidsBlockedListener listener : jidsBlockedListeners) { 111 listener.onJidsBlocked(blockedJids); 112 } 113 114 return IQ.createResultIQ(blockContactIQ); 115 } 116 }); 117 118 // unblock IQ handler 119 connection.registerIQRequestHandler(new AbstractIqRequestHandler(UnblockContactsIQ.ELEMENT, 120 UnblockContactsIQ.NAMESPACE, Type.set, Mode.sync) { 121 @Override 122 public IQ handleIQRequest(IQ iqRequest) { 123 UnblockContactsIQ unblockContactIQ = (UnblockContactsIQ) iqRequest; 124 125 if (blockListCached == null) { 126 blockListCached = new ArrayList<Jid>(); 127 } 128 129 List<Jid> unblockedJids = unblockContactIQ.getJids(); 130 if (unblockedJids == null) { // remove all 131 blockListCached.clear(); 132 for (AllJidsUnblockedListener listener : allJidsUnblockedListeners) { 133 listener.onAllJidsUnblocked(); 134 } 135 } else { // remove only some 136 blockListCached.removeAll(unblockedJids); 137 for (JidsUnblockedListener listener : jidsUnblockedListeners) { 138 listener.onJidsUnblocked(unblockedJids); 139 } 140 } 141 142 return IQ.createResultIQ(unblockContactIQ); 143 } 144 }); 145 146 connection.addConnectionListener(new AbstractConnectionListener() { 147 @Override 148 public void authenticated(XMPPConnection connection, boolean resumed) { 149 // No need to reset the cache if the connection got resumed. 150 if (resumed) { 151 return; 152 } 153 blockListCached = null; 154 } 155 }); 156 } 157 158 /** 159 * Returns true if Blocking Command is supported by the server. 160 * 161 * @return true if Blocking Command is supported by the server. 162 * @throws NoResponseException 163 * @throws XMPPErrorException 164 * @throws NotConnectedException 165 * @throws InterruptedException 166 */ 167 public boolean isSupportedByServer() 168 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 169 return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(NAMESPACE); 170 } 171 172 /** 173 * Returns the block list. 174 * 175 * @return the blocking list 176 * @throws NoResponseException 177 * @throws XMPPErrorException 178 * @throws NotConnectedException 179 * @throws InterruptedException 180 */ 181 public List<Jid> getBlockList() 182 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 183 184 if (blockListCached == null) { 185 BlockListIQ blockListIQ = new BlockListIQ(); 186 BlockListIQ blockListIQResult = connection().createStanzaCollectorAndSend(blockListIQ).nextResultOrThrow(); 187 blockListCached = blockListIQResult.getBlockedJidsCopy(); 188 } 189 190 return Collections.unmodifiableList(blockListCached); 191 } 192 193 /** 194 * Block contacts. 195 * 196 * @param jids 197 * @throws NoResponseException 198 * @throws XMPPErrorException 199 * @throws NotConnectedException 200 * @throws InterruptedException 201 */ 202 public void blockContacts(List<Jid> jids) 203 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 204 BlockContactsIQ blockContactIQ = new BlockContactsIQ(jids); 205 connection().createStanzaCollectorAndSend(blockContactIQ).nextResultOrThrow(); 206 } 207 208 /** 209 * Unblock contacts. 210 * 211 * @param jids 212 * @throws NoResponseException 213 * @throws XMPPErrorException 214 * @throws NotConnectedException 215 * @throws InterruptedException 216 */ 217 public void unblockContacts(List<Jid> jids) 218 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 219 UnblockContactsIQ unblockContactIQ = new UnblockContactsIQ(jids); 220 connection().createStanzaCollectorAndSend(unblockContactIQ).nextResultOrThrow(); 221 } 222 223 /** 224 * Unblock all. 225 * 226 * @throws NoResponseException 227 * @throws XMPPErrorException 228 * @throws NotConnectedException 229 * @throws InterruptedException 230 */ 231 public void unblockAll() 232 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 233 UnblockContactsIQ unblockContactIQ = new UnblockContactsIQ(); 234 connection().createStanzaCollectorAndSend(unblockContactIQ).nextResultOrThrow(); 235 } 236 237 public void addJidsBlockedListener(JidsBlockedListener jidsBlockedListener) { 238 jidsBlockedListeners.add(jidsBlockedListener); 239 } 240 241 public void removeJidsBlockedListener(JidsBlockedListener jidsBlockedListener) { 242 jidsBlockedListeners.remove(jidsBlockedListener); 243 } 244 245 public void addJidsUnblockedListener(JidsUnblockedListener jidsUnblockedListener) { 246 jidsUnblockedListeners.add(jidsUnblockedListener); 247 } 248 249 public void removeJidsUnblockedListener(JidsUnblockedListener jidsUnblockedListener) { 250 jidsUnblockedListeners.remove(jidsUnblockedListener); 251 } 252 253 public void addAllJidsUnblockedListener(AllJidsUnblockedListener allJidsUnblockedListener) { 254 allJidsUnblockedListeners.add(allJidsUnblockedListener); 255 } 256 257 public void removeAllJidsUnblockedListener(AllJidsUnblockedListener allJidsUnblockedListener) { 258 allJidsUnblockedListeners.remove(allJidsUnblockedListener); 259 } 260}