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 TODO javadoc me please
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 TODO javadoc me please
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 TODO javadoc me please
107     * @return true if Multi-User Chat Light feature is supported by the server.
108     * @throws NotConnectedException if the XMPP connection is not connected.
109     * @throws XMPPErrorException if there was an XMPP error returned.
110     * @throws NoResponseException if there was no response from the remote entity.
111     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
123     * @return a List of the rooms the user occupies.
124     * @throws XMPPErrorException if there was an XMPP error returned.
125     * @throws NoResponseException if there was no response from the remote entity.
126     * @throws NotConnectedException if the XMPP connection is not connected.
127     * @throws InterruptedException if the calling thread was interrupted.
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 if there was an XMPP error returned.
148     * @throws NoResponseException if there was no response from the remote entity.
149     * @throws NotConnectedException if the XMPP connection is not connected.
150     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
162     * @return the list of users and rooms blocked
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 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 TODO javadoc me please
188     * @return the list of rooms blocked
189     * @throws NoResponseException if there was no response from the remote entity.
190     * @throws XMPPErrorException if there was an XMPP error returned.
191     * @throws NotConnectedException if the XMPP connection is not connected.
192     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
210     * @return the list of users blocked
211     * @throws NoResponseException if there was no response from the remote entity.
212     * @throws XMPPErrorException if there was an XMPP error returned.
213     * @throws NotConnectedException if the XMPP connection is not connected.
214     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
246     * @param roomJid TODO javadoc me please
247     * @throws NoResponseException if there was no response from the remote entity.
248     * @throws XMPPErrorException if there was an XMPP error returned.
249     * @throws NotConnectedException if the XMPP connection is not connected.
250     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
263     * @param roomsJids TODO javadoc me please
264     * @throws NoResponseException if there was no response from the remote entity.
265     * @throws XMPPErrorException if there was an XMPP error returned.
266     * @throws NotConnectedException if the XMPP connection is not connected.
267     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
290     * @param userJid TODO javadoc me please
291     * @throws NoResponseException if there was no response from the remote entity.
292     * @throws XMPPErrorException if there was an XMPP error returned.
293     * @throws NotConnectedException if the XMPP connection is not connected.
294     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
307     * @param usersJids TODO javadoc me please
308     * @throws NoResponseException if there was no response from the remote entity.
309     * @throws XMPPErrorException if there was an XMPP error returned.
310     * @throws NotConnectedException if the XMPP connection is not connected.
311     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
334     * @param roomJid TODO javadoc me please
335     * @throws NoResponseException if there was no response from the remote entity.
336     * @throws XMPPErrorException if there was an XMPP error returned.
337     * @throws NotConnectedException if the XMPP connection is not connected.
338     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
351     * @param roomsJids TODO javadoc me please
352     * @throws NoResponseException if there was no response from the remote entity.
353     * @throws XMPPErrorException if there was an XMPP error returned.
354     * @throws NotConnectedException if the XMPP connection is not connected.
355     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
378     * @param userJid TODO javadoc me please
379     * @throws NoResponseException if there was no response from the remote entity.
380     * @throws XMPPErrorException if there was an XMPP error returned.
381     * @throws NotConnectedException if the XMPP connection is not connected.
382     * @throws InterruptedException if the calling thread was interrupted.
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 TODO javadoc me please
395     * @param usersJids TODO javadoc me please
396     * @throws NoResponseException if there was no response from the remote entity.
397     * @throws XMPPErrorException if there was an XMPP error returned.
398     * @throws NotConnectedException if the XMPP connection is not connected.
399     * @throws InterruptedException if the calling thread was interrupted.
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}