001/**
002 *
003 * Copyright 2016 Fernando Ramirez
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.muclight;
018
019import java.lang.ref.WeakReference;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.WeakHashMap;
025
026import org.jivesoftware.smack.Manager;
027import org.jivesoftware.smack.SmackException.NoResponseException;
028import org.jivesoftware.smack.SmackException.NotConnectedException;
029import org.jivesoftware.smack.XMPPConnection;
030import org.jivesoftware.smack.XMPPException.XMPPErrorException;
031import org.jivesoftware.smack.filter.IQReplyFilter;
032import org.jivesoftware.smack.filter.StanzaFilter;
033import org.jivesoftware.smack.packet.IQ;
034
035import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
036import org.jivesoftware.smackx.disco.packet.DiscoverItems;
037import org.jivesoftware.smackx.muclight.element.MUCLightBlockingIQ;
038
039import org.jxmpp.jid.DomainBareJid;
040import org.jxmpp.jid.EntityBareJid;
041import org.jxmpp.jid.Jid;
042
043/**
044 * Multi-User Chat Light manager class.
045 *
046 * @author Fernando Ramirez
047 *
048 */
049public final class MultiUserChatLightManager extends Manager {
050
051    private static final Map<XMPPConnection, MultiUserChatLightManager> INSTANCES = new WeakHashMap<XMPPConnection, MultiUserChatLightManager>();
052
053    /**
054     * Get a instance of a MUC Light manager for the given connection.
055     *
056     * @param connection TODO javadoc me please
057     * @return a MUCLight manager.
058     */
059    public static synchronized MultiUserChatLightManager getInstanceFor(XMPPConnection connection) {
060        MultiUserChatLightManager multiUserChatLightManager = INSTANCES.get(connection);
061        if (multiUserChatLightManager == null) {
062            multiUserChatLightManager = new MultiUserChatLightManager(connection);
063            INSTANCES.put(connection, multiUserChatLightManager);
064        }
065        return multiUserChatLightManager;
066    }
067
068    /**
069     * A Map of MUC Light JIDs to instances. We use weak references for the
070     * values in order to allow those instances to get garbage collected.
071     */
072    private final Map<EntityBareJid, WeakReference<MultiUserChatLight>> multiUserChatLights = new HashMap<>();
073
074    private MultiUserChatLightManager(XMPPConnection connection) {
075        super(connection);
076    }
077
078    /**
079     * Obtain the MUC Light.
080     *
081     * @param jid TODO javadoc me please
082     * @return the MUCLight.
083     */
084    public synchronized MultiUserChatLight getMultiUserChatLight(EntityBareJid jid) {
085        WeakReference<MultiUserChatLight> weakRefMultiUserChat = multiUserChatLights.get(jid);
086        if (weakRefMultiUserChat == null) {
087            return createNewMucLightAndAddToMap(jid);
088        }
089        MultiUserChatLight multiUserChatLight = weakRefMultiUserChat.get();
090        if (multiUserChatLight == null) {
091            return createNewMucLightAndAddToMap(jid);
092        }
093        return multiUserChatLight;
094    }
095
096    private MultiUserChatLight createNewMucLightAndAddToMap(EntityBareJid jid) {
097        MultiUserChatLight multiUserChatLight = new MultiUserChatLight(connection(), jid);
098        multiUserChatLights.put(jid, new WeakReference<>(multiUserChatLight));
099        return multiUserChatLight;
100    }
101
102    /**
103     * Returns true if Multi-User Chat Light feature is supported by the server.
104     *
105     * @param mucLightService TODO javadoc me please
106     * @return true if Multi-User Chat Light feature is supported by the server.
107     * @throws NotConnectedException if the XMPP connection is not connected.
108     * @throws XMPPErrorException if there was an XMPP error returned.
109     * @throws NoResponseException if there was no response from the remote entity.
110     * @throws InterruptedException if the calling thread was interrupted.
111     */
112    public boolean isFeatureSupported(DomainBareJid mucLightService)
113            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
114        return ServiceDiscoveryManager.getInstanceFor(connection()).discoverInfo(mucLightService)
115                .containsFeature(MultiUserChatLight.NAMESPACE);
116    }
117
118    /**
119     * Returns a List of the rooms the user occupies.
120     *
121     * @param mucLightService TODO javadoc me please
122     * @return a List of the rooms the user occupies.
123     * @throws XMPPErrorException if there was an XMPP error returned.
124     * @throws NoResponseException if there was no response from the remote entity.
125     * @throws NotConnectedException if the XMPP connection is not connected.
126     * @throws InterruptedException if the calling thread was interrupted.
127     */
128    public List<Jid> getOccupiedRooms(DomainBareJid mucLightService)
129            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
130        DiscoverItems result = ServiceDiscoveryManager.getInstanceFor(connection()).discoverItems(mucLightService);
131        List<DiscoverItems.Item> items = result.getItems();
132        List<Jid> answer = new ArrayList<>(items.size());
133
134        for (DiscoverItems.Item item : items) {
135            Jid mucLight = item.getEntityID();
136            answer.add(mucLight);
137        }
138
139        return answer;
140    }
141
142    /**
143     * Returns a collection with the XMPP addresses of the MUC Light services.
144     *
145     * @return a collection with the XMPP addresses of the MUC Light services.
146     * @throws XMPPErrorException if there was an XMPP error returned.
147     * @throws NoResponseException if there was no response from the remote entity.
148     * @throws NotConnectedException if the XMPP connection is not connected.
149     * @throws InterruptedException if the calling thread was interrupted.
150     */
151    public List<DomainBareJid> getLocalServices()
152            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
153        ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
154        return sdm.findServices(MultiUserChatLight.NAMESPACE, false, false);
155    }
156
157    /**
158     * Get users and rooms blocked.
159     *
160     * @param mucLightService TODO javadoc me please
161     * @return the list of users and rooms blocked
162     * @throws NoResponseException if there was no response from the remote entity.
163     * @throws XMPPErrorException if there was an XMPP error returned.
164     * @throws NotConnectedException if the XMPP connection is not connected.
165     * @throws InterruptedException if the calling thread was interrupted.
166     */
167    public List<Jid> getUsersAndRoomsBlocked(DomainBareJid mucLightService)
168            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
169        MUCLightBlockingIQ muclIghtBlockingIQResult = getBlockingList(mucLightService);
170
171        List<Jid> jids = new ArrayList<>();
172        if (muclIghtBlockingIQResult.getRooms() != null) {
173            jids.addAll(muclIghtBlockingIQResult.getRooms().keySet());
174        }
175
176        if (muclIghtBlockingIQResult.getUsers() != null) {
177            jids.addAll(muclIghtBlockingIQResult.getUsers().keySet());
178        }
179
180        return jids;
181    }
182
183    /**
184     * Get rooms blocked.
185     *
186     * @param mucLightService TODO javadoc me please
187     * @return the list of rooms blocked
188     * @throws NoResponseException if there was no response from the remote entity.
189     * @throws XMPPErrorException if there was an XMPP error returned.
190     * @throws NotConnectedException if the XMPP connection is not connected.
191     * @throws InterruptedException if the calling thread was interrupted.
192     */
193    public List<Jid> getRoomsBlocked(DomainBareJid mucLightService)
194            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
195        MUCLightBlockingIQ mucLightBlockingIQResult = getBlockingList(mucLightService);
196
197        List<Jid> jids = new ArrayList<>();
198        if (mucLightBlockingIQResult.getRooms() != null) {
199            jids.addAll(mucLightBlockingIQResult.getRooms().keySet());
200        }
201
202        return jids;
203    }
204
205    /**
206     * Get users blocked.
207     *
208     * @param mucLightService TODO javadoc me please
209     * @return the list of users blocked
210     * @throws NoResponseException if there was no response from the remote entity.
211     * @throws XMPPErrorException if there was an XMPP error returned.
212     * @throws NotConnectedException if the XMPP connection is not connected.
213     * @throws InterruptedException if the calling thread was interrupted.
214     */
215    public List<Jid> getUsersBlocked(DomainBareJid mucLightService)
216            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
217        MUCLightBlockingIQ mucLightBlockingIQResult = getBlockingList(mucLightService);
218
219        List<Jid> jids = new ArrayList<>();
220        if (mucLightBlockingIQResult.getUsers() != null) {
221            jids.addAll(mucLightBlockingIQResult.getUsers().keySet());
222        }
223
224        return jids;
225    }
226
227    private MUCLightBlockingIQ getBlockingList(DomainBareJid mucLightService)
228            throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException {
229        MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, null);
230        mucLightBlockingIQ.setType(IQ.Type.get);
231        mucLightBlockingIQ.setTo(mucLightService);
232
233        StanzaFilter responseFilter = new IQReplyFilter(mucLightBlockingIQ, connection());
234        IQ responseIq = connection().createStanzaCollectorAndSend(responseFilter, mucLightBlockingIQ)
235                .nextResultOrThrow();
236        MUCLightBlockingIQ mucLightBlockingIQResult = (MUCLightBlockingIQ) responseIq;
237
238        return mucLightBlockingIQResult;
239    }
240
241    /**
242     * Block a room.
243     *
244     * @param mucLightService TODO javadoc me please
245     * @param roomJid TODO javadoc me please
246     * @throws NoResponseException if there was no response from the remote entity.
247     * @throws XMPPErrorException if there was an XMPP error returned.
248     * @throws NotConnectedException if the XMPP connection is not connected.
249     * @throws InterruptedException if the calling thread was interrupted.
250     */
251    public void blockRoom(DomainBareJid mucLightService, Jid roomJid)
252            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
253        HashMap<Jid, Boolean> rooms = new HashMap<>();
254        rooms.put(roomJid, false);
255        sendBlockRooms(mucLightService, rooms);
256    }
257
258    /**
259     * Block rooms.
260     *
261     * @param mucLightService TODO javadoc me please
262     * @param roomsJids TODO javadoc me please
263     * @throws NoResponseException if there was no response from the remote entity.
264     * @throws XMPPErrorException if there was an XMPP error returned.
265     * @throws NotConnectedException if the XMPP connection is not connected.
266     * @throws InterruptedException if the calling thread was interrupted.
267     */
268    public void blockRooms(DomainBareJid mucLightService, List<Jid> roomsJids)
269            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
270        HashMap<Jid, Boolean> rooms = new HashMap<>();
271        for (Jid jid : roomsJids) {
272            rooms.put(jid, false);
273        }
274        sendBlockRooms(mucLightService, rooms);
275    }
276
277    private void sendBlockRooms(DomainBareJid mucLightService, HashMap<Jid, Boolean> rooms)
278            throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException {
279        MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(rooms, null);
280        mucLightBlockingIQ.setType(IQ.Type.set);
281        mucLightBlockingIQ.setTo(mucLightService);
282        connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ);
283    }
284
285    /**
286     * Block a user.
287     *
288     * @param mucLightService TODO javadoc me please
289     * @param userJid TODO javadoc me please
290     * @throws NoResponseException if there was no response from the remote entity.
291     * @throws XMPPErrorException if there was an XMPP error returned.
292     * @throws NotConnectedException if the XMPP connection is not connected.
293     * @throws InterruptedException if the calling thread was interrupted.
294     */
295    public void blockUser(DomainBareJid mucLightService, Jid userJid)
296            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
297        HashMap<Jid, Boolean> users = new HashMap<>();
298        users.put(userJid, false);
299        sendBlockUsers(mucLightService, users);
300    }
301
302    /**
303     * Block users.
304     *
305     * @param mucLightService TODO javadoc me please
306     * @param usersJids TODO javadoc me please
307     * @throws NoResponseException if there was no response from the remote entity.
308     * @throws XMPPErrorException if there was an XMPP error returned.
309     * @throws NotConnectedException if the XMPP connection is not connected.
310     * @throws InterruptedException if the calling thread was interrupted.
311     */
312    public void blockUsers(DomainBareJid mucLightService, List<Jid> usersJids)
313            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
314        HashMap<Jid, Boolean> users = new HashMap<>();
315        for (Jid jid : usersJids) {
316            users.put(jid, false);
317        }
318        sendBlockUsers(mucLightService, users);
319    }
320
321    private void sendBlockUsers(DomainBareJid mucLightService, HashMap<Jid, Boolean> users)
322            throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException {
323        MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, users);
324        mucLightBlockingIQ.setType(IQ.Type.set);
325        mucLightBlockingIQ.setTo(mucLightService);
326        connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ);
327    }
328
329    /**
330     * Unblock a room.
331     *
332     * @param mucLightService TODO javadoc me please
333     * @param roomJid TODO javadoc me please
334     * @throws NoResponseException if there was no response from the remote entity.
335     * @throws XMPPErrorException if there was an XMPP error returned.
336     * @throws NotConnectedException if the XMPP connection is not connected.
337     * @throws InterruptedException if the calling thread was interrupted.
338     */
339    public void unblockRoom(DomainBareJid mucLightService, Jid roomJid)
340            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
341        HashMap<Jid, Boolean> rooms = new HashMap<>();
342        rooms.put(roomJid, true);
343        sendUnblockRooms(mucLightService, rooms);
344    }
345
346    /**
347     * Unblock rooms.
348     *
349     * @param mucLightService TODO javadoc me please
350     * @param roomsJids TODO javadoc me please
351     * @throws NoResponseException if there was no response from the remote entity.
352     * @throws XMPPErrorException if there was an XMPP error returned.
353     * @throws NotConnectedException if the XMPP connection is not connected.
354     * @throws InterruptedException if the calling thread was interrupted.
355     */
356    public void unblockRooms(DomainBareJid mucLightService, List<Jid> roomsJids)
357            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
358        HashMap<Jid, Boolean> rooms = new HashMap<>();
359        for (Jid jid : roomsJids) {
360            rooms.put(jid, true);
361        }
362        sendUnblockRooms(mucLightService, rooms);
363    }
364
365    private void sendUnblockRooms(DomainBareJid mucLightService, HashMap<Jid, Boolean> rooms)
366            throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException {
367        MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(rooms, null);
368        mucLightBlockingIQ.setType(IQ.Type.set);
369        mucLightBlockingIQ.setTo(mucLightService);
370        connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ);
371    }
372
373    /**
374     * Unblock a user.
375     *
376     * @param mucLightService TODO javadoc me please
377     * @param userJid TODO javadoc me please
378     * @throws NoResponseException if there was no response from the remote entity.
379     * @throws XMPPErrorException if there was an XMPP error returned.
380     * @throws NotConnectedException if the XMPP connection is not connected.
381     * @throws InterruptedException if the calling thread was interrupted.
382     */
383    public void unblockUser(DomainBareJid mucLightService, Jid userJid)
384            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
385        HashMap<Jid, Boolean> users = new HashMap<>();
386        users.put(userJid, true);
387        sendUnblockUsers(mucLightService, users);
388    }
389
390    /**
391     * Unblock users.
392     *
393     * @param mucLightService TODO javadoc me please
394     * @param usersJids TODO javadoc me please
395     * @throws NoResponseException if there was no response from the remote entity.
396     * @throws XMPPErrorException if there was an XMPP error returned.
397     * @throws NotConnectedException if the XMPP connection is not connected.
398     * @throws InterruptedException if the calling thread was interrupted.
399     */
400    public void unblockUsers(DomainBareJid mucLightService, List<Jid> usersJids)
401            throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
402        HashMap<Jid, Boolean> users = new HashMap<>();
403        for (Jid jid : usersJids) {
404            users.put(jid, true);
405        }
406        sendUnblockUsers(mucLightService, users);
407    }
408
409    private void sendUnblockUsers(DomainBareJid mucLightService, HashMap<Jid, Boolean> users)
410            throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException {
411        MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, users);
412        mucLightBlockingIQ.setType(IQ.Type.set);
413        mucLightBlockingIQ.setTo(mucLightService);
414        connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ);
415    }
416
417}