blob: 4dcc9e1cbec959943417942b8bb7081e947b6021 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * $Revision$
3 * $Date$
4 *
5 * Copyright 2006-2007 Jive Software.
6 *
7 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20package org.jivesoftware.smack;
21
22import org.jivesoftware.smack.filter.*;
23import org.jivesoftware.smack.packet.IQ;
24import org.jivesoftware.smack.packet.Packet;
25import org.jivesoftware.smack.packet.Privacy;
26import org.jivesoftware.smack.packet.PrivacyItem;
27
28import java.util.*;
29
30/**
31 * A PrivacyListManager is used by XMPP clients to block or allow communications from other
32 * users. Use the manager to: <ul>
33 * <li>Retrieve privacy lists.
34 * <li>Add, remove, and edit privacy lists.
35 * <li>Set, change, or decline active lists.
36 * <li>Set, change, or decline the default list (i.e., the list that is active by default).
37 * </ul>
38 * Privacy Items can handle different kind of permission communications based on JID, group,
39 * subscription type or globally (@see PrivacyItem).
40 *
41 * @author Francisco Vives
42 */
43public class PrivacyListManager {
44
45 // Keep the list of instances of this class.
46 private static Map<Connection, PrivacyListManager> instances = Collections
47 .synchronizedMap(new WeakHashMap<Connection, PrivacyListManager>());
48
49 private Connection connection;
50 private final List<PrivacyListListener> listeners = new ArrayList<PrivacyListListener>();
51 PacketFilter packetFilter = new AndFilter(new IQTypeFilter(IQ.Type.SET),
52 new PacketExtensionFilter("query", "jabber:iq:privacy"));
53
54 static {
55 // Create a new PrivacyListManager on every established connection. In the init()
56 // method of PrivacyListManager, we'll add a listener that will delete the
57 // instance when the connection is closed.
58 Connection.addConnectionCreationListener(new ConnectionCreationListener() {
59 public void connectionCreated(Connection connection) {
60 new PrivacyListManager(connection);
61 }
62 });
63 }
64 /**
65 * Creates a new privacy manager to maintain the communication privacy. Note: no
66 * information is sent to or received from the server until you attempt to
67 * get or set the privacy communication.<p>
68 *
69 * @param connection the XMPP connection.
70 */
71 private PrivacyListManager(Connection connection) {
72 this.connection = connection;
73 this.init();
74 }
75
76 /** Answer the connection userJID that owns the privacy.
77 * @return the userJID that owns the privacy
78 */
79 private String getUser() {
80 return connection.getUser();
81 }
82
83 /**
84 * Initializes the packet listeners of the connection that will notify for any set privacy
85 * package.
86 */
87 private void init() {
88 // Register the new instance and associate it with the connection
89 instances.put(connection, this);
90 // Add a listener to the connection that removes the registered instance when
91 // the connection is closed
92 connection.addConnectionListener(new ConnectionListener() {
93 public void connectionClosed() {
94 // Unregister this instance since the connection has been closed
95 instances.remove(connection);
96 }
97
98 public void connectionClosedOnError(Exception e) {
99 // ignore
100 }
101
102 public void reconnectionFailed(Exception e) {
103 // ignore
104 }
105
106 public void reconnectingIn(int seconds) {
107 // ignore
108 }
109
110 public void reconnectionSuccessful() {
111 // ignore
112 }
113 });
114
115 connection.addPacketListener(new PacketListener() {
116 public void processPacket(Packet packet) {
117
118 if (packet == null || packet.getError() != null) {
119 return;
120 }
121 // The packet is correct.
122 Privacy privacy = (Privacy) packet;
123
124 // Notifies the event to the listeners.
125 synchronized (listeners) {
126 for (PrivacyListListener listener : listeners) {
127 // Notifies the created or updated privacy lists
128 for (Map.Entry<String,List<PrivacyItem>> entry : privacy.getItemLists().entrySet()) {
129 String listName = entry.getKey();
130 List<PrivacyItem> items = entry.getValue();
131 if (items.isEmpty()) {
132 listener.updatedPrivacyList(listName);
133 } else {
134 listener.setPrivacyList(listName, items);
135 }
136 }
137 }
138 }
139
140 // Send a result package acknowledging the reception of a privacy package.
141
142 // Prepare the IQ packet to send
143 IQ iq = new IQ() {
144 public String getChildElementXML() {
145 return "";
146 }
147 };
148 iq.setType(IQ.Type.RESULT);
149 iq.setFrom(packet.getFrom());
150 iq.setPacketID(packet.getPacketID());
151
152 // Send create & join packet.
153 connection.sendPacket(iq);
154 }
155 }, packetFilter);
156 }
157
158 /**
159 * Returns the PrivacyListManager instance associated with a given Connection.
160 *
161 * @param connection the connection used to look for the proper PrivacyListManager.
162 * @return the PrivacyListManager associated with a given Connection.
163 */
164 public static PrivacyListManager getInstanceFor(Connection connection) {
165 return instances.get(connection);
166 }
167
168 /**
169 * Send the {@link Privacy} packet to the server in order to know some privacy content and then
170 * waits for the answer.
171 *
172 * @param requestPrivacy is the {@link Privacy} packet configured properly whose XML
173 * will be sent to the server.
174 * @return a new {@link Privacy} with the data received from the server.
175 * @exception XMPPException if the request or the answer failed, it raises an exception.
176 */
177 private Privacy getRequest(Privacy requestPrivacy) throws XMPPException {
178 // The request is a get iq type
179 requestPrivacy.setType(Privacy.Type.GET);
180 requestPrivacy.setFrom(this.getUser());
181
182 // Filter packets looking for an answer from the server.
183 PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID());
184 PacketCollector response = connection.createPacketCollector(responseFilter);
185
186 // Send create & join packet.
187 connection.sendPacket(requestPrivacy);
188
189 // Wait up to a certain number of seconds for a reply.
190 Privacy privacyAnswer =
191 (Privacy) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
192
193 // Stop queuing results
194 response.cancel();
195
196 // Interprete the result and answer the privacy only if it is valid
197 if (privacyAnswer == null) {
198 throw new XMPPException("No response from server.");
199 }
200 else if (privacyAnswer.getError() != null) {
201 throw new XMPPException(privacyAnswer.getError());
202 }
203 return privacyAnswer;
204 }
205
206 /**
207 * Send the {@link Privacy} packet to the server in order to modify the server privacy and
208 * waits for the answer.
209 *
210 * @param requestPrivacy is the {@link Privacy} packet configured properly whose xml will be sent
211 * to the server.
212 * @return a new {@link Privacy} with the data received from the server.
213 * @exception XMPPException if the request or the answer failed, it raises an exception.
214 */
215 private Packet setRequest(Privacy requestPrivacy) throws XMPPException {
216
217 // The request is a get iq type
218 requestPrivacy.setType(Privacy.Type.SET);
219 requestPrivacy.setFrom(this.getUser());
220
221 // Filter packets looking for an answer from the server.
222 PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID());
223 PacketCollector response = connection.createPacketCollector(responseFilter);
224
225 // Send create & join packet.
226 connection.sendPacket(requestPrivacy);
227
228 // Wait up to a certain number of seconds for a reply.
229 Packet privacyAnswer = response.nextResult(SmackConfiguration.getPacketReplyTimeout());
230
231 // Stop queuing results
232 response.cancel();
233
234 // Interprete the result and answer the privacy only if it is valid
235 if (privacyAnswer == null) {
236 throw new XMPPException("No response from server.");
237 } else if (privacyAnswer.getError() != null) {
238 throw new XMPPException(privacyAnswer.getError());
239 }
240 return privacyAnswer;
241 }
242
243 /**
244 * Answer a privacy containing the list structre without {@link PrivacyItem}.
245 *
246 * @return a Privacy with the list names.
247 * @throws XMPPException if an error occurs.
248 */
249 private Privacy getPrivacyWithListNames() throws XMPPException {
250
251 // The request of the list is an empty privacy message
252 Privacy request = new Privacy();
253
254 // Send the package to the server and get the answer
255 return getRequest(request);
256 }
257
258 /**
259 * Answer the active privacy list.
260 *
261 * @return the privacy list of the active list.
262 * @throws XMPPException if an error occurs.
263 */
264 public PrivacyList getActiveList() throws XMPPException {
265 Privacy privacyAnswer = this.getPrivacyWithListNames();
266 String listName = privacyAnswer.getActiveName();
267 boolean isDefaultAndActive = privacyAnswer.getActiveName() != null
268 && privacyAnswer.getDefaultName() != null
269 && privacyAnswer.getActiveName().equals(
270 privacyAnswer.getDefaultName());
271 return new PrivacyList(true, isDefaultAndActive, listName, getPrivacyListItems(listName));
272 }
273
274 /**
275 * Answer the default privacy list.
276 *
277 * @return the privacy list of the default list.
278 * @throws XMPPException if an error occurs.
279 */
280 public PrivacyList getDefaultList() throws XMPPException {
281 Privacy privacyAnswer = this.getPrivacyWithListNames();
282 String listName = privacyAnswer.getDefaultName();
283 boolean isDefaultAndActive = privacyAnswer.getActiveName() != null
284 && privacyAnswer.getDefaultName() != null
285 && privacyAnswer.getActiveName().equals(
286 privacyAnswer.getDefaultName());
287 return new PrivacyList(isDefaultAndActive, true, listName, getPrivacyListItems(listName));
288 }
289
290 /**
291 * Answer the privacy list items under listName with the allowed and blocked permissions.
292 *
293 * @param listName the name of the list to get the allowed and blocked permissions.
294 * @return a list of privacy items under the list listName.
295 * @throws XMPPException if an error occurs.
296 */
297 private List<PrivacyItem> getPrivacyListItems(String listName) throws XMPPException {
298
299 // The request of the list is an privacy message with an empty list
300 Privacy request = new Privacy();
301 request.setPrivacyList(listName, new ArrayList<PrivacyItem>());
302
303 // Send the package to the server and get the answer
304 Privacy privacyAnswer = getRequest(request);
305
306 return privacyAnswer.getPrivacyList(listName);
307 }
308
309 /**
310 * Answer the privacy list items under listName with the allowed and blocked permissions.
311 *
312 * @param listName the name of the list to get the allowed and blocked permissions.
313 * @return a privacy list under the list listName.
314 * @throws XMPPException if an error occurs.
315 */
316 public PrivacyList getPrivacyList(String listName) throws XMPPException {
317
318 return new PrivacyList(false, false, listName, getPrivacyListItems(listName));
319 }
320
321 /**
322 * Answer every privacy list with the allowed and blocked permissions.
323 *
324 * @return an array of privacy lists.
325 * @throws XMPPException if an error occurs.
326 */
327 public PrivacyList[] getPrivacyLists() throws XMPPException {
328 Privacy privacyAnswer = this.getPrivacyWithListNames();
329 Set<String> names = privacyAnswer.getPrivacyListNames();
330 PrivacyList[] lists = new PrivacyList[names.size()];
331 boolean isActiveList;
332 boolean isDefaultList;
333 int index=0;
334 for (String listName : names) {
335 isActiveList = listName.equals(privacyAnswer.getActiveName());
336 isDefaultList = listName.equals(privacyAnswer.getDefaultName());
337 lists[index] = new PrivacyList(isActiveList, isDefaultList,
338 listName, getPrivacyListItems(listName));
339 index = index + 1;
340 }
341 return lists;
342 }
343
344
345 /**
346 * Set or change the active list to listName.
347 *
348 * @param listName the list name to set as the active one.
349 * @exception XMPPException if the request or the answer failed, it raises an exception.
350 */
351 public void setActiveListName(String listName) throws XMPPException {
352
353 // The request of the list is an privacy message with an empty list
354 Privacy request = new Privacy();
355 request.setActiveName(listName);
356
357 // Send the package to the server
358 setRequest(request);
359 }
360
361 /**
362 * Client declines the use of active lists.
363 *
364 * @throws XMPPException if an error occurs.
365 */
366 public void declineActiveList() throws XMPPException {
367
368 // The request of the list is an privacy message with an empty list
369 Privacy request = new Privacy();
370 request.setDeclineActiveList(true);
371
372 // Send the package to the server
373 setRequest(request);
374 }
375
376 /**
377 * Set or change the default list to listName.
378 *
379 * @param listName the list name to set as the default one.
380 * @exception XMPPException if the request or the answer failed, it raises an exception.
381 */
382 public void setDefaultListName(String listName) throws XMPPException {
383
384 // The request of the list is an privacy message with an empty list
385 Privacy request = new Privacy();
386 request.setDefaultName(listName);
387
388 // Send the package to the server
389 setRequest(request);
390 }
391
392 /**
393 * Client declines the use of default lists.
394 *
395 * @throws XMPPException if an error occurs.
396 */
397 public void declineDefaultList() throws XMPPException {
398
399 // The request of the list is an privacy message with an empty list
400 Privacy request = new Privacy();
401 request.setDeclineDefaultList(true);
402
403 // Send the package to the server
404 setRequest(request);
405 }
406
407 /**
408 * The client has created a new list. It send the new one to the server.
409 *
410 * @param listName the list that has changed its content.
411 * @param privacyItems a List with every privacy item in the list.
412 * @throws XMPPException if an error occurs.
413 */
414 public void createPrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException {
415
416 this.updatePrivacyList(listName, privacyItems);
417 }
418
419 /**
420 * The client has edited an existing list. It updates the server content with the resulting
421 * list of privacy items. The {@link PrivacyItem} list MUST contain all elements in the
422 * list (not the "delta").
423 *
424 * @param listName the list that has changed its content.
425 * @param privacyItems a List with every privacy item in the list.
426 * @throws XMPPException if an error occurs.
427 */
428 public void updatePrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException {
429
430 // Build the privacy package to add or update the new list
431 Privacy request = new Privacy();
432 request.setPrivacyList(listName, privacyItems);
433
434 // Send the package to the server
435 setRequest(request);
436 }
437
438 /**
439 * Remove a privacy list.
440 *
441 * @param listName the list that has changed its content.
442 * @throws XMPPException if an error occurs.
443 */
444 public void deletePrivacyList(String listName) throws XMPPException {
445
446 // The request of the list is an privacy message with an empty list
447 Privacy request = new Privacy();
448 request.setPrivacyList(listName, new ArrayList<PrivacyItem>());
449
450 // Send the package to the server
451 setRequest(request);
452 }
453
454 /**
455 * Adds a packet listener that will be notified of any new update in the user
456 * privacy communication.
457 *
458 * @param listener a packet listener.
459 */
460 public void addListener(PrivacyListListener listener) {
461 // Keep track of the listener so that we can manually deliver extra
462 // messages to it later if needed.
463 synchronized (listeners) {
464 listeners.add(listener);
465 }
466 }
467}