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