001/** 002 * 003 * Copyright 2016-2017 Fernando Ramirez, 2016-2020 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.ConnectionCreationListener; 028import org.jivesoftware.smack.ConnectionListener; 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; 038 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; 043 044import org.jxmpp.jid.Jid; 045 046/** 047 * Blocking command manager class. 048 * 049 * @author Fernando Ramirez 050 * @author Florian Schmaus 051 * @see <a href="http://xmpp.org/extensions/xep-0191.html">XEP-0191: Blocking 052 * Command</a> 053 */ 054public final class BlockingCommandManager extends Manager { 055 056 public static final String NAMESPACE = "urn:xmpp:blocking"; 057 058 private volatile List<Jid> blockListCached; 059 060 static { 061 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 062 @Override 063 public void connectionCreated(XMPPConnection connection) { 064 getInstanceFor(connection); 065 } 066 }); 067 } 068 069 private static final Map<XMPPConnection, BlockingCommandManager> INSTANCES = new WeakHashMap<>(); 070 071 /** 072 * Get the singleton instance of BlockingCommandManager. 073 * 074 * @param connection TODO javadoc me please 075 * @return the instance of BlockingCommandManager 076 */ 077 public static synchronized BlockingCommandManager getInstanceFor(XMPPConnection connection) { 078 BlockingCommandManager blockingCommandManager = INSTANCES.get(connection); 079 080 if (blockingCommandManager == null) { 081 blockingCommandManager = new BlockingCommandManager(connection); 082 INSTANCES.put(connection, blockingCommandManager); 083 } 084 085 return blockingCommandManager; 086 } 087 088 private final Set<AllJidsUnblockedListener> allJidsUnblockedListeners = new CopyOnWriteArraySet<>(); 089 090 private final Set<JidsBlockedListener> jidsBlockedListeners = new CopyOnWriteArraySet<>(); 091 092 private final Set<JidsUnblockedListener> jidsUnblockedListeners = new CopyOnWriteArraySet<>(); 093 094 private BlockingCommandManager(XMPPConnection connection) { 095 super(connection); 096 097 // block IQ handler 098 connection.registerIQRequestHandler( 099 new AbstractIqRequestHandler(BlockContactsIQ.ELEMENT, BlockContactsIQ.NAMESPACE, IQ.Type.set, Mode.sync) { 100 @Override 101 public IQ handleIQRequest(IQ iqRequest) { 102 BlockContactsIQ blockContactIQ = (BlockContactsIQ) iqRequest; 103 104 if (blockListCached == null) { 105 blockListCached = new ArrayList<>(); 106 } 107 108 List<Jid> blockedJids = blockContactIQ.getJids(); 109 blockListCached.addAll(blockedJids); 110 111 for (JidsBlockedListener listener : jidsBlockedListeners) { 112 listener.onJidsBlocked(blockedJids); 113 } 114 115 return IQ.createResultIQ(blockContactIQ); 116 } 117 }); 118 119 // unblock IQ handler 120 connection.registerIQRequestHandler(new AbstractIqRequestHandler(UnblockContactsIQ.ELEMENT, 121 UnblockContactsIQ.NAMESPACE, IQ.Type.set, Mode.sync) { 122 @Override 123 public IQ handleIQRequest(IQ iqRequest) { 124 UnblockContactsIQ unblockContactIQ = (UnblockContactsIQ) iqRequest; 125 126 if (blockListCached == null) { 127 blockListCached = new ArrayList<>(); 128 } 129 130 List<Jid> unblockedJids = unblockContactIQ.getJids(); 131 if (unblockedJids == null) { // remove all 132 blockListCached.clear(); 133 for (AllJidsUnblockedListener listener : allJidsUnblockedListeners) { 134 listener.onAllJidsUnblocked(); 135 } 136 } else { // remove only some 137 blockListCached.removeAll(unblockedJids); 138 for (JidsUnblockedListener listener : jidsUnblockedListeners) { 139 listener.onJidsUnblocked(unblockedJids); 140 } 141 } 142 143 return IQ.createResultIQ(unblockContactIQ); 144 } 145 }); 146 147 connection.addConnectionListener(new ConnectionListener() { 148 @Override 149 public void authenticated(XMPPConnection connection, boolean resumed) { 150 // No need to reset the cache if the connection got resumed. 151 if (resumed) { 152 return; 153 } 154 blockListCached = null; 155 } 156 }); 157 } 158 159 /** 160 * Returns true if Blocking Command is supported by the server. 161 * 162 * @return true if Blocking Command is supported by the server. 163 * @throws NoResponseException if there was no response from the remote entity. 164 * @throws XMPPErrorException if there was an XMPP error returned. 165 * @throws NotConnectedException if the XMPP connection is not connected. 166 * @throws InterruptedException if the calling thread was interrupted. 167 */ 168 public boolean isSupportedByServer() 169 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 170 return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(NAMESPACE); 171 } 172 173 /** 174 * Returns the block list. 175 * 176 * @return the blocking list 177 * @throws NoResponseException if there was no response from the remote entity. 178 * @throws XMPPErrorException if there was an XMPP error returned. 179 * @throws NotConnectedException if the XMPP connection is not connected. 180 * @throws InterruptedException if the calling thread was interrupted. 181 */ 182 public List<Jid> getBlockList() 183 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 184 185 if (blockListCached == null) { 186 BlockListIQ blockListIQ = new BlockListIQ(); 187 BlockListIQ blockListIQResult = connection().sendIqRequestAndWaitForResponse(blockListIQ); 188 blockListCached = blockListIQResult.getBlockedJidsCopy(); 189 } 190 191 return Collections.unmodifiableList(blockListCached); 192 } 193 194 /** 195 * Block contacts. 196 * 197 * @param jids TODO javadoc me please 198 * @throws NoResponseException if there was no response from the remote entity. 199 * @throws XMPPErrorException if there was an XMPP error returned. 200 * @throws NotConnectedException if the XMPP connection is not connected. 201 * @throws InterruptedException if the calling thread was interrupted. 202 */ 203 public void blockContacts(List<Jid> jids) 204 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 205 BlockContactsIQ blockContactIQ = new BlockContactsIQ(jids); 206 connection().sendIqRequestAndWaitForResponse(blockContactIQ); 207 } 208 209 /** 210 * Unblock contacts. 211 * 212 * @param jids TODO javadoc me please 213 * @throws NoResponseException if there was no response from the remote entity. 214 * @throws XMPPErrorException if there was an XMPP error returned. 215 * @throws NotConnectedException if the XMPP connection is not connected. 216 * @throws InterruptedException if the calling thread was interrupted. 217 */ 218 public void unblockContacts(List<Jid> jids) 219 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 220 UnblockContactsIQ unblockContactIQ = new UnblockContactsIQ(jids); 221 connection().sendIqRequestAndWaitForResponse(unblockContactIQ); 222 } 223 224 /** 225 * Unblock all. 226 * 227 * @throws NoResponseException if there was no response from the remote entity. 228 * @throws XMPPErrorException if there was an XMPP error returned. 229 * @throws NotConnectedException if the XMPP connection is not connected. 230 * @throws InterruptedException if the calling thread was interrupted. 231 */ 232 public void unblockAll() 233 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 234 UnblockContactsIQ unblockContactIQ = new UnblockContactsIQ(); 235 connection().sendIqRequestAndWaitForResponse(unblockContactIQ); 236 } 237 238 public void addJidsBlockedListener(JidsBlockedListener jidsBlockedListener) { 239 jidsBlockedListeners.add(jidsBlockedListener); 240 } 241 242 public void removeJidsBlockedListener(JidsBlockedListener jidsBlockedListener) { 243 jidsBlockedListeners.remove(jidsBlockedListener); 244 } 245 246 public void addJidsUnblockedListener(JidsUnblockedListener jidsUnblockedListener) { 247 jidsUnblockedListeners.add(jidsUnblockedListener); 248 } 249 250 public void removeJidsUnblockedListener(JidsUnblockedListener jidsUnblockedListener) { 251 jidsUnblockedListeners.remove(jidsUnblockedListener); 252 } 253 254 public void addAllJidsUnblockedListener(AllJidsUnblockedListener allJidsUnblockedListener) { 255 allJidsUnblockedListeners.add(allJidsUnblockedListener); 256 } 257 258 public void removeAllJidsUnblockedListener(AllJidsUnblockedListener allJidsUnblockedListener) { 259 allJidsUnblockedListeners.remove(allJidsUnblockedListener); 260 } 261}