1 /*
2  * Copyright (C) 2005-2008 Jive Software. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.jivesoftware.openfire.handler;
18 
19 import org.dom4j.Element;
20 import org.jivesoftware.openfire.IQHandlerInfo;
21 import org.jivesoftware.openfire.XMPPServer;
22 import org.jivesoftware.openfire.auth.UnauthorizedException;
23 import org.jivesoftware.openfire.disco.ServerFeaturesProvider;
24 import org.jivesoftware.openfire.event.UserEventListener;
25 import org.jivesoftware.openfire.privacy.PrivacyList;
26 import org.jivesoftware.openfire.privacy.PrivacyListManager;
27 import org.jivesoftware.openfire.privacy.PrivacyListProvider;
28 import org.jivesoftware.openfire.session.ClientSession;
29 import org.jivesoftware.openfire.user.User;
30 import org.jivesoftware.openfire.user.UserManager;
31 import org.xmpp.packet.IQ;
32 import org.xmpp.packet.JID;
33 import org.xmpp.packet.PacketError;
34 
35 import java.util.Collections;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 
40 /**
41  * IQPrivacyHandler is responsible for handling privacy lists.
42  *
43  * @author Gaston Dombiak
44  */
45 public class IQPrivacyHandler extends IQHandler
46         implements ServerFeaturesProvider, UserEventListener {
47 
48     private IQHandlerInfo info;
49     private PrivacyListManager manager = PrivacyListManager.getInstance();
50     private PrivacyListProvider provider = PrivacyListProvider.getInstance();
51 
IQPrivacyHandler()52     public IQPrivacyHandler() {
53         super("Blocking Communication Handler");
54         info = new IQHandlerInfo("query", "jabber:iq:privacy");
55     }
56 
57     @Override
handleIQ(IQ packet)58     public IQ handleIQ(IQ packet) throws UnauthorizedException {
59         IQ.Type type = packet.getType();
60         JID from = packet.getFrom();
61         if (from.getNode() == null || !UserManager.getInstance().isRegisteredUser(from, false)) {
62             // Service is unavailable for anonymous users
63             IQ result = IQ.createResultIQ(packet);
64             result.setChildElement(packet.getChildElement().createCopy());
65             result.setError(PacketError.Condition.service_unavailable);
66             return result;
67         }
68         IQ result = null;
69         if (type.equals(IQ.Type.get)) {
70             // User wants to retrieve a privacy list or the list of privacy list
71             Element child = packet.getChildElement();
72             List elements = child.elements();
73             if (elements.isEmpty()) {
74                 // User requested names of privacy lists
75                 result = getPrivacyListsNames(packet, from);
76             }
77             else {
78                 // User requested a privacy list
79                 result = getPrivacyList(packet, from);
80             }
81         }
82         else if (type.equals(IQ.Type.set)) {
83             Element child = packet.getChildElement();
84             Element activeList = child.element("active");
85             Element defaultList = child.element("default");
86             if (activeList != null) {
87                 // Active list handling
88                 String listName = activeList.attributeValue("name");
89                 if (listName != null) {
90                     // User wants to set or change the active list currently being applied by
91                     // the server to this session
92                     result = setActiveList(packet, from, listName);
93                 }
94                 else {
95                     // User wants to decline the use of any active list for this session
96                     result = declineActiveList(packet, from);
97 
98                 }
99             }
100             else if (defaultList != null) {
101                 // Default list handling
102                 String listName = defaultList.attributeValue("name");
103                 if (listName != null) {
104                     // User wants to set or change its default list (i.e. which applies
105                     // to the user as a whole, not only the sending resource)
106                     result = setDefaultList(packet, from, listName);
107                 }
108                 else {
109                     // User wants to decline the use of a default list
110                     result = declineDefaultList(packet, from);
111                 }
112             }
113             else {
114                 // Privacy list handling (create/edit/delete)
115                 Element list = child.element("list");
116                 String listName = list.attributeValue("name");
117                 List items = list.elements();
118                 if (!items.isEmpty()) {
119                     // User wants to create or edit a privacy list
120                     result = updateOrCreateList(packet, from, list);
121                 }
122                 else {
123                     // User wants to delete a privacy list
124                     result = deleteList(packet, from, listName);
125 
126                 }
127             }
128         }
129         return result;
130     }
131 
132     /**
133      * Returns the IQ packet containing the active and default lists and the lists
134      * defined by the user.
135      *
136      * @param packet IQ packet requesting the lists.
137      * @param from sender of the IQ packet.
138      * @return the IQ packet containing the active and default lists and the lists
139      *         defined by the user.
140      */
getPrivacyListsNames(IQ packet, JID from)141     private IQ getPrivacyListsNames(IQ packet, JID from) {
142         IQ result = IQ.createResultIQ(packet);
143         Element childElement = packet.getChildElement().createCopy();
144         result.setChildElement(childElement);
145         Map<String, Boolean> privacyLists = provider.getPrivacyLists(from.getNode());
146         // Add the default list
147         for (String listName : privacyLists.keySet()) {
148             if (privacyLists.get(listName)) {
149                 childElement.addElement("default").addAttribute("name", listName);
150             }
151         }
152         // Add the active list (only if there is an active list for the session)
153         ClientSession session = sessionManager.getSession(from);
154         if  (session != null && session.getActiveList() != null) {
155             childElement.addElement("active")
156                     .addAttribute("name", session.getActiveList().getName());
157         }
158 
159         // Add a list element for each privacy list
160         for (String listName : privacyLists.keySet()) {
161             childElement.addElement("list").addAttribute("name", listName);
162         }
163         return result;
164     }
165 
166     /**
167      * Returns the IQ packet containing the details of the specified list. If no list
168      * was found or the IQ request contains more than one specified list then an error will
169      * be returned.
170      *
171      * @param packet IQ packet requesting a given list.
172      * @param from sender of the IQ packet.
173      * @return the IQ packet containing the details of the specified list.
174      */
getPrivacyList(IQ packet, JID from)175     private IQ getPrivacyList(IQ packet, JID from) {
176         IQ result = IQ.createResultIQ(packet);
177         Element childElement = packet.getChildElement().createCopy();
178         result.setChildElement(childElement);
179 
180         // Check that only one list was requested
181         List<Element> lists = childElement.elements("list");
182         if (lists.size() > 1) {
183             result.setError(PacketError.Condition.bad_request);
184         }
185         else {
186             String listName = lists.get(0).attributeValue("name");
187             PrivacyList list = null;
188             if (listName != null) {
189                 // A list name was specified so get it
190                 list = manager.getPrivacyList(from.getNode(), listName);
191             }
192             if (list != null) {
193                 // Add the privacy list to the result
194                 childElement = result.setChildElement("query", "jabber:iq:privacy");
195                 childElement.add(list.asElement());
196             }
197             else {
198                 // List not found
199                 result.setError(PacketError.Condition.item_not_found);
200             }
201         }
202         return result;
203     }
204 
205     /**
206      * User has specified a new active list that should be used for the current session.
207      *
208      * @param packet IQ packet setting new active list for the current session.
209      * @param from sender of the IQ packet.
210      * @param listName name of the new active list for the current session.
211      * @return acknowledge of success.
212      */
setActiveList(IQ packet, JID from, String listName)213     private IQ setActiveList(IQ packet, JID from, String listName) {
214         IQ result = IQ.createResultIQ(packet);
215         Element childElement = packet.getChildElement().createCopy();
216         result.setChildElement(childElement);
217 
218         // Get the list
219         PrivacyList list = manager.getPrivacyList(from.getNode(), listName);
220         if (list != null) {
221             // Get the user session
222             ClientSession session = sessionManager.getSession(from);
223             if (session != null) {
224                 // Set the new active list for this session
225                 session.setActiveList(list);
226             }
227         }
228         else {
229             // List not found
230             result.setError(PacketError.Condition.item_not_found);
231         }
232         return result;
233     }
234 
235     /**
236      * User has requested that no active list should be used for the current session. Return
237      * acknowledge of success.
238      *
239      * @param packet IQ packet declining active list for the current session.
240      * @param from sender of the IQ packet.
241      * @return acknowledge of success.
242      */
declineActiveList(IQ packet, JID from)243     private IQ declineActiveList(IQ packet, JID from) {
244         // Get the user session
245         ClientSession session = sessionManager.getSession(from);
246         // Set that there is no active list for this session
247         session.setActiveList(null);
248         // Return acknowledge of success
249         return IQ.createResultIQ(packet);
250     }
251 
252     /**
253      * User has specified a new default list that should be used for all session.
254      *
255      * @param packet IQ packet setting new default list for all sessions.
256      * @param from sender of the IQ packet.
257      * @param listName name of the new default list for all sessions.
258      * @return acknowledge of success.
259      */
setDefaultList(IQ packet, JID from, String listName)260     private IQ setDefaultList(IQ packet, JID from, String listName) {
261         IQ result = IQ.createResultIQ(packet);
262         Element childElement = packet.getChildElement().createCopy();
263         result.setChildElement(childElement);
264 
265         if (sessionManager.getSessionCount(from.getNode()) > 1) {
266             // Current default list is being used by more than one session
267             result.setError(PacketError.Condition.conflict);
268         }
269         else {
270             // Get the list
271             PrivacyList list = manager.getPrivacyList(from.getNode(), listName);
272             if (list != null) {
273                 // Get the user session
274                 ClientSession session = sessionManager.getSession(from);
275                 PrivacyList oldDefaultList = session.getDefaultList();
276                 manager.changeDefaultList(from.getNode(), list, oldDefaultList);
277                 // Set the new default list for this session (the only existing session)
278                 session.setDefaultList(list);
279             }
280             else {
281                 // List not found
282                 result.setError(PacketError.Condition.item_not_found);
283             }
284         }
285         return result;
286     }
287 
288     /**
289      * User has specified that there is no default list that should be used for this user.
290      *
291      * @param packet IQ packet declining default list for all sessions.
292      * @param from sender of the IQ packet.
293      * @return acknowledge of success.
294      */
declineDefaultList(IQ packet, JID from)295     private IQ declineDefaultList(IQ packet, JID from) {
296         IQ result = IQ.createResultIQ(packet);
297         Element childElement = packet.getChildElement().createCopy();
298         result.setChildElement(childElement);
299 
300         if (sessionManager.getSessionCount(from.getNode()) > 1) {
301             // Current default list is being used by more than one session
302             result.setError(PacketError.Condition.conflict);
303         }
304         else {
305             // Get the user session
306             ClientSession session = sessionManager.getSession(from);
307             // Check if a default list was already defined
308             if (session.getDefaultList() != null) {
309                 // Set the existing default list as non-default
310                 session.getDefaultList().setDefaultList(false);
311                 // Update the database with the new list state
312                 provider.updatePrivacyList(from.getNode(), session.getDefaultList());
313                 session.setDefaultList(null);
314             }
315         }
316         return result;
317     }
318 
319     /**
320      * Updates an existing privacy list or creates a new one with the specified items list. The
321      * new list will not become the active or default list by default. The user will have to
322      * send another packet to set the new list as active or default.<p>
323      *
324      * Once the list was updated or created a "privacy list push" will be sent to all
325      * connected resources of the user.
326      *
327      * @param packet IQ packet updating or creating a new privacy list.
328      * @param from sender of the IQ packet.
329      * @param listElement the element containing the list and its items.
330      * @return acknowledge of success.
331      */
updateOrCreateList(IQ packet, JID from, Element listElement)332     private IQ updateOrCreateList(IQ packet, JID from, Element listElement) {
333         IQ result = IQ.createResultIQ(packet);
334         Element childElement = packet.getChildElement().createCopy();
335         result.setChildElement(childElement);
336 
337         String listName = listElement.attributeValue("name");
338         PrivacyList list = manager.getPrivacyList(from.getNode(), listName);
339         if (list == null) {
340             list = manager.createPrivacyList(from.getNode(), listName, listElement);
341         }
342         else {
343             // Update existing list
344             list.updateList(listElement);
345             provider.updatePrivacyList(from.getNode(), list);
346             // Make sure that existing user sessions that are using the updated list are poining
347             // to the updated instance. This may happen since PrivacyListManager uses a Cache that
348             // may expire so it's possible to have many instances representing the same privacy
349             // list. Therefore, if a list is modified then we need to make sure that all
350             // instances are replaced with the updated instance. An OR Mapping Tool would have
351             // avoided this issue since identity is ensured.
352             for (ClientSession session : sessionManager.getSessions(from.getNode())) {
353                 if (list.equals(session.getDefaultList())) {
354                     session.setDefaultList(list);
355                 }
356                 if (list.equals(session.getActiveList())) {
357                     session.setActiveList(list);
358                 }
359             }
360         }
361         // Send a "privacy list push" to all connected resources
362         IQ pushPacket = new IQ(IQ.Type.set);
363         Element child = pushPacket.setChildElement("query", "jabber:iq:privacy");
364         child.addElement("list").addAttribute("name", list.getName());
365         sessionManager.userBroadcast(from.getNode(), pushPacket);
366 
367         return result;
368     }
369 
deleteList(IQ packet, JID from, String listName)370     private IQ deleteList(IQ packet, JID from, String listName) {
371         ClientSession currentSession;
372         IQ result = IQ.createResultIQ(packet);
373         Element childElement = packet.getChildElement().createCopy();
374         result.setChildElement(childElement);
375         // Get the list to delete
376         PrivacyList list = manager.getPrivacyList(from.getNode(), listName);
377 
378         if (list == null) {
379             // List to delete was not found
380             result.setError(PacketError.Condition.item_not_found);
381             return result;
382         }
383         else {
384             currentSession = sessionManager.getSession(from);
385             // Check if the list is being used by another session
386             for (ClientSession session : sessionManager.getSessions(from.getNode())) {
387                 if (currentSession == session) {
388                     // Ignore the active session for this checking
389                     continue;
390                 }
391                 if (list.equals(session.getDefaultList()) || list.equals(session.getActiveList())) {
392                     // List to delete is being used by another session so return a conflict error
393                     result.setError(PacketError.Condition.conflict);
394                     return result;
395                 }
396             }
397         }
398         // Remove the list from the active session (if it was being used)
399         if (list.equals(currentSession.getDefaultList())) {
400             currentSession.setDefaultList(null);
401         }
402         if (list.equals(currentSession.getActiveList())) {
403             currentSession.setActiveList(null);
404         }
405         manager.deletePrivacyList(from.getNode(), listName);
406         return result;
407     }
408 
409     @Override
getInfo()410     public IQHandlerInfo getInfo() {
411         return info;
412     }
413 
414     @Override
getFeatures()415     public Iterator<String> getFeatures() {
416         return Collections.singleton("jabber:iq:privacy").iterator();
417     }
418 
419     @Override
userCreated(User user, Map params)420     public void userCreated(User user, Map params) {
421         //Do nothing
422     }
423 
424     @Override
userDeleting(User user, Map params)425     public void userDeleting(User user, Map params) {
426         // Delete privacy lists owned by the user being deleted
427         manager.deletePrivacyLists(user.getUsername());
428     }
429 
430     @Override
userModified(User user, Map params)431     public void userModified(User user, Map params) {
432         //Do nothing
433     }
434 
435     @Override
initialize(XMPPServer server)436     public void initialize(XMPPServer server) {
437         super.initialize(server);
438     }
439 }
440