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