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