1 7 8 package org.jivesoftware.messenger.plugin; 9 10 import java.io.File ; 11 import java.util.ArrayList ; 12 import java.util.Arrays ; 13 import java.util.Collection ; 14 import java.util.Enumeration ; 15 import java.util.HashSet ; 16 import java.util.Hashtable ; 17 import java.util.Iterator ; 18 import java.util.List ; 19 import java.util.Map ; 20 21 import org.dom4j.Attribute; 22 import org.dom4j.DocumentHelper; 23 import org.dom4j.Element; 24 import org.dom4j.QName; 25 import org.jivesoftware.messenger.XMPPServer; 26 import org.jivesoftware.messenger.container.Plugin; 27 import org.jivesoftware.messenger.container.PluginManager; 28 import org.jivesoftware.messenger.forms.DataForm; 29 import org.jivesoftware.messenger.forms.FormField; 30 import org.jivesoftware.messenger.forms.spi.XDataFormImpl; 31 import org.jivesoftware.messenger.forms.spi.XFormFieldImpl; 32 import org.jivesoftware.messenger.user.User; 33 import org.jivesoftware.messenger.user.UserManager; 34 import org.jivesoftware.messenger.user.UserNotFoundException; 35 import org.jivesoftware.util.JiveGlobals; 36 import org.jivesoftware.util.Log; 37 import org.jivesoftware.util.PropertyEventDispatcher; 38 import org.jivesoftware.util.PropertyEventListener; 39 import org.xmpp.component.Component; 40 import org.xmpp.component.ComponentException; 41 import org.xmpp.component.ComponentManager; 42 import org.xmpp.component.ComponentManagerFactory; 43 import org.xmpp.packet.IQ; 44 import org.xmpp.packet.JID; 45 import org.xmpp.packet.Packet; 46 47 61 public class SearchPlugin implements Component, Plugin, PropertyEventListener { 62 private XMPPServer server; 63 private UserManager userManager; 64 private ComponentManager componentManager; 65 private PluginManager pluginManager; 66 67 private String serviceName; 68 private boolean serviceEnabled; 69 70 private static String serverName; 71 72 private static String instructions = "The following fields are available for search. " 73 + "Wildcard (*) characters are allowed as part the of query."; 74 75 private static Element probeResult; 76 77 private Collection <String > searchFields; 78 79 public SearchPlugin() { 80 serviceName = JiveGlobals.getProperty("plugin.search.serviceName", "search"); 81 serviceEnabled = JiveGlobals.getBooleanProperty("plugin.search.serviceEnabled", true); 82 83 server = XMPPServer.getInstance(); 84 serverName = server.getServerInfo().getName(); 85 UserManager manager = UserManager.getInstance(); 88 try { 89 searchFields = manager.getSearchFields(); 90 userManager = UserManager.getInstance(); 91 searchFields = userManager.getSearchFields(); 92 } 93 catch (UnsupportedOperationException uoe) { 94 searchFields = getSearchFields(); 96 } 97 } 98 99 public String getName() { 100 return pluginManager.getName(this); 101 } 102 103 public String getDescription() { 104 return pluginManager.getDescription(this); 105 } 106 107 public void initializePlugin(PluginManager manager, File pluginDirectory) { 108 pluginManager = manager; 109 110 componentManager = ComponentManagerFactory.getComponentManager(); 111 try { 112 componentManager.addComponent(serviceName, this); 113 } 114 catch (Exception e) { 115 componentManager.getLog().error(e); 116 } 117 PropertyEventDispatcher.addListener(this); 118 119 if (probeResult == null) { 120 probeResult = DocumentHelper.createElement(QName.get("query", "jabber:iq:search")); 121 122 probeResult.addElement("instructions").addText(instructions); 124 125 XDataFormImpl searchForm = new XDataFormImpl(DataForm.TYPE_FORM); 126 searchForm.setTitle("User Search"); 127 searchForm.addInstruction(instructions); 128 129 XFormFieldImpl field = new XFormFieldImpl("FORM_TYPE"); 130 field.setType(FormField.TYPE_HIDDEN); 131 field.addValue("jabber:iq:search"); 132 searchForm.addField(field); 133 134 field = new XFormFieldImpl("search"); 135 field.setType(FormField.TYPE_TEXT_SINGLE); 136 field.setLabel("Search"); 137 field.setRequired(false); 138 searchForm.addField(field); 139 140 for (String searchField : searchFields) { 141 probeResult.addElement(searchField.toLowerCase()); 143 144 field = new XFormFieldImpl(searchField); 145 field.setType(FormField.TYPE_BOOLEAN); 146 field.addValue("1"); 147 field.setLabel(searchField); 148 field.setRequired(false); 149 searchForm.addField(field); 150 } 151 152 probeResult.add(searchForm.asXMLElement()); 153 } 154 } 155 156 public void initialize(JID jid, ComponentManager componentManager) { 157 } 158 159 public void start() { 160 } 161 162 public void destroyPlugin() { 163 PropertyEventDispatcher.removeListener(this); 164 pluginManager = null; 165 try { 166 componentManager.removeComponent(serviceName); 167 componentManager = null; 168 } 169 catch (Exception e) { 170 componentManager.getLog().error(e); 171 } 172 server = null; 173 userManager = null; 174 } 175 176 public void shutdown() { 177 } 178 179 public void processPacket(Packet p) { 180 if (p instanceof IQ) { 181 IQ packet = (IQ) p; 182 183 Element childElement = (packet).getChildElement(); 184 String namespace = null; 185 if (childElement != null) { 186 namespace = childElement.getNamespaceURI(); 187 } 188 189 if ("jabber:iq:search".equals(namespace)) { 190 try { 191 IQ replyPacket = handleIQ(packet); 192 componentManager.sendPacket(this, replyPacket); 193 } 194 catch (ComponentException e) { 195 componentManager.getLog().error(e); 196 } 197 198 } 199 else if ("http://jabber.org/protocol/disco#info".equals(namespace)) { 200 try { 201 IQ replyPacket = IQ.createResultIQ(packet); 202 203 Element responseElement = DocumentHelper.createElement(QName.get( 204 "query", "http://jabber.org/protocol/disco#info")); 205 responseElement.addElement("identity").addAttribute("category", "search") 206 .addAttribute("type", "text") 207 .addAttribute("name", "User Search"); 208 responseElement.addElement("feature").addAttribute("var", "jabber:iq:search"); 209 replyPacket.setChildElement(responseElement); 210 211 componentManager.sendPacket(this, replyPacket); 212 } 213 catch (ComponentException e) { 214 componentManager.getLog().error(e); 215 } 216 } 217 else if ("http://jabber.org/protocol/disco#items".equals(namespace)) { 218 try { 219 IQ replyPacket = IQ.createResultIQ(packet); 220 Element responseElement = DocumentHelper.createElement(QName.get( 221 "query", "http://jabber.org/protocol/disco#info")); 222 223 replyPacket.setChildElement(responseElement); 224 componentManager.sendPacket(this, replyPacket); 225 } 226 catch (ComponentException e) { 227 componentManager.getLog().error(e); 228 } 229 } 230 } 231 } 232 233 private IQ handleIQ(IQ packet) { 234 if (!serviceEnabled) { 235 return replyDisabled(packet); 236 } 237 238 if (IQ.Type.get.equals(packet.getType())) { 239 return processGetPacket(packet); 240 } 241 else if (IQ.Type.set.equals(packet.getType())) { 242 return processSetPacket(packet); 243 } 244 245 return null; 246 } 247 248 private IQ replyDisabled(IQ packet) { 249 Element reply = DocumentHelper.createElement(QName.get("query", "jabber:iq:search")); 250 XDataFormImpl unavailableForm = new XDataFormImpl(DataForm.TYPE_CANCEL); 251 unavailableForm.setTitle("User Search"); 252 unavailableForm.addInstruction("This service is unavailable."); 253 reply.add(unavailableForm.asXMLElement()); 254 255 IQ replyPacket = IQ.createResultIQ(packet); 256 replyPacket.setChildElement("query", "jabber:iq:search"); 257 replyPacket.setChildElement(reply.createCopy()); 258 259 return replyPacket; 260 } 261 262 private IQ processGetPacket(IQ packet) { 263 IQ replyPacket = IQ.createResultIQ(packet); 264 replyPacket.setChildElement("query", "jabber:iq:search"); 265 replyPacket.setChildElement(probeResult.createCopy()); 266 267 return replyPacket; 268 } 269 270 private IQ processSetPacket(IQ packet) { 271 List <User> users = new ArrayList <User>(); 272 273 Element incomingForm = packet.getChildElement(); 274 boolean isDataFormQuery = (incomingForm.element(QName.get("x", "jabber:x:data")) != null); 275 276 Hashtable <String , String > searchList = extractSearchQuery(incomingForm); 277 Enumeration <String > searchIter = searchList.keys(); 278 while (searchIter.hasMoreElements()) { 279 String field = (String ) searchIter.nextElement(); 280 String query = (String ) searchList.get(field); 281 282 Collection <User> foundUsers = new ArrayList <User>(); 283 if (userManager != null) { 284 if (query.length() > 0 && !query.equals("jabber:iq:search")) { 285 foundUsers.addAll(userManager.findUsers(new HashSet <String >( 286 Arrays.asList((field))), query)); 287 } 288 } 289 else { 290 foundUsers.addAll(findUsers(field, query)); 291 } 292 293 for (User user : foundUsers) { 295 if (!users.contains(user)) { 296 users.add(user); 297 } 298 } 299 } 300 301 if (isDataFormQuery) { 302 return replyDataFormResult(users, packet); 303 } 304 else { 305 return replyNonDataFormResult(users, packet); 306 } 307 } 308 309 313 private Hashtable <String , String > extractSearchQuery(Element incomingForm) { 314 Hashtable <String , String > searchList = new Hashtable <String , String >(); 315 Element form = incomingForm.element(QName.get("x", "jabber:x:data")); 316 if (form == null) { 317 Element name = incomingForm.element("name"); 318 if (name != null) { 319 searchList.put("Name", name.getTextTrim()); 320 } 321 322 Element nick = incomingForm.element("nick"); 323 if (nick != null) { 324 searchList.put("Username", nick.getTextTrim()); 325 } 326 327 Element first = incomingForm.element("first"); 328 if (first != null) { 329 searchList.put("Name", first.getTextTrim()); 330 } 331 332 Element last = incomingForm.element("last"); 333 if (last != null) { 334 searchList.put("Name", last.getTextTrim()); 335 } 336 337 Element email = incomingForm.element("email"); 338 if (email != null) { 339 searchList.put("Email", email.getTextTrim()); 340 } 341 } 342 else { 343 List <String > searchFields = new ArrayList <String >(); 344 String search = ""; 345 346 Iterator fields = form.elementIterator("field"); 347 while (fields.hasNext()) { 348 Element searchField = (Element) fields.next(); 349 350 String field = searchField.attributeValue("var"); 351 String value = searchField.element("value").getTextTrim(); 352 if (field.equals("search")) { 353 search = value; 354 } 355 else if (value.equals("1")) { 356 searchFields.add(field); 357 } 358 } 359 360 for (String field : searchFields) { 361 searchList.put(field, search); 362 } 363 } 364 365 return searchList; 366 } 367 368 private IQ replyDataFormResult(List <User> users, IQ packet) { 369 XDataFormImpl searchResults = new XDataFormImpl(DataForm.TYPE_RESULT); 370 371 XFormFieldImpl field = new XFormFieldImpl("FORM_TYPE"); 372 field.setType(FormField.TYPE_HIDDEN); 373 searchResults.addField(field); 374 375 field = new XFormFieldImpl("jid"); 376 field.setLabel("JID"); 377 searchResults.addReportedField(field); 378 379 for (String fieldName : searchFields) { 380 field = new XFormFieldImpl(fieldName); 381 field.setLabel(fieldName); 382 searchResults.addReportedField(field); 383 } 384 385 for (User user : users) { 386 String username = user.getUsername(); 387 388 ArrayList <XFormFieldImpl> items = new ArrayList <XFormFieldImpl>(); 389 390 XFormFieldImpl fieldJID = new XFormFieldImpl("jid"); 391 fieldJID.addValue(username + "@" + serverName); 392 items.add(fieldJID); 393 394 XFormFieldImpl fieldUsername = new XFormFieldImpl("Username"); 395 fieldUsername.addValue(username); 396 items.add(fieldUsername); 397 398 XFormFieldImpl fieldName = new XFormFieldImpl("Name"); 399 fieldName.addValue(user.getName()); 400 items.add(fieldName); 401 402 XFormFieldImpl fieldEmail = new XFormFieldImpl("Email"); 403 fieldEmail.addValue(user.getEmail()); 404 items.add(fieldEmail); 405 406 searchResults.addItemFields(items); 407 } 408 409 Element reply = DocumentHelper.createElement(QName.get("query", "jabber:iq:search")); 410 reply.add(searchResults.asXMLElement()); 411 412 IQ replyPacket = IQ.createResultIQ(packet); 413 replyPacket.setChildElement(reply); 414 415 return replyPacket; 416 } 417 418 private IQ replyNonDataFormResult(List <User> users, IQ packet) { 419 Element replyQuery = DocumentHelper.createElement(QName.get("query", "jabber:iq:search")); 420 String serverName = XMPPServer.getInstance().getServerInfo().getName(); 421 422 for (User user : users) { 423 Element item = DocumentHelper.createElement("item"); 424 Attribute jib = DocumentHelper.createAttribute(item, "jid", user.getUsername() + "@" + serverName); 425 item.add(jib); 426 Element nick = DocumentHelper.createElement("nick"); 427 nick.addText(user.getName()); 428 item.add(nick); 429 Element email = DocumentHelper.createElement("email"); 430 email.addText(user.getEmail()); 431 item.add(email); 432 replyQuery.add(item); 433 } 434 IQ replyPacket = IQ.createResultIQ(packet); 435 replyPacket.setChildElement(replyQuery); 436 437 return replyPacket; 438 } 439 440 public String getServiceName() { 441 return serviceName; 442 } 443 444 public void setServiceName(String name) { 445 changeServiceName(name); 446 JiveGlobals.setProperty("plugin.search.serviceName", name); 447 } 448 449 public boolean getServiceEnabled() { 450 return serviceEnabled; 451 } 452 453 public void setServiceEnabled(boolean enabled) { 454 serviceEnabled = enabled; 455 JiveGlobals.setProperty("plugin.search.serviceEnabled", enabled ? "true" : "false"); 456 } 457 458 public void propertySet(String property, Map params) { 459 if (property.equals("plugin.search.serviceEnabled")) { 460 this.serviceEnabled = Boolean.parseBoolean((String )params.get("value")); 461 } 462 else if (property.equals("plugin.search.serviceName")) { 463 changeServiceName((String )params.get("value")); 464 } 465 } 466 467 public void propertyDeleted(String property, Map params) { 468 if (property.equals("plugin.search.serviceEnabled")) { 469 this.serviceEnabled = true; 470 } 471 else if (property.equals("plugin.search.serviceName")) { 472 changeServiceName("search"); 473 } 474 } 475 476 public void xmlPropertySet(String property, Map params) { 477 } 479 480 public void xmlPropertyDeleted(String property, Map params) { 481 } 483 484 private void changeServiceName(String serviceName) { 485 if (serviceName == null) { 486 throw new NullPointerException ("Service name cannot be null"); 487 } 488 489 if (this.serviceName.equals(serviceName)) { 490 return; 491 } 492 493 try { 495 componentManager.removeComponent(this.serviceName); 496 } 497 catch (Exception e) { 498 componentManager.getLog().error(e); 499 } 500 501 try { 502 componentManager.addComponent(serviceName, this); 503 } 504 catch (Exception e) { 505 componentManager.getLog().error(e); 506 } 507 508 this.serviceName = serviceName; 509 } 510 511 516 public Collection <String > getSearchFields() { 517 return Arrays.asList("Username", "Name", "Email"); 518 } 519 520 530 public Collection <User> findUsers(String field, String query) { 531 List <User> foundUsers = new ArrayList <User>(); 532 533 if (!getSearchFields().contains(field)) { 534 return foundUsers; 535 } 536 537 int index = query.indexOf("*"); 538 if (index == -1) { 539 Collection <User> users = userManager.getUsers(); 540 for (User user : users) { 541 if (field.equals("Username")) { 542 try { 543 foundUsers.add(userManager.getUser(query)); 544 return foundUsers; 545 } 546 catch (UserNotFoundException e) { 547 Log.error("Error getting user", e); 548 } 549 } 550 else if (field.equals("Name")) { 551 if (query.equalsIgnoreCase(user.getName())) { 552 foundUsers.add(user); 553 } 554 } 555 else if (field.equals("Email")) { 556 if (user.getEmail() != null) { 557 if (query.equalsIgnoreCase(user.getEmail())) { 558 foundUsers.add(user); 559 } 560 } 561 } 562 } 563 } 564 else { 565 String prefix = query.substring(0, index); 566 Collection <User> users = userManager.getUsers(); 567 for (User user : users) { 568 String userInfo = ""; 569 if (field.equals("Username")) { 570 userInfo = user.getUsername(); 571 } 572 else if (field.equals("Name")) { 573 userInfo = user.getName(); 574 } 575 else if (field.equals("Email")) { 576 userInfo = user.getEmail() == null ? "" : user.getEmail(); 577 } 578 579 if (index < userInfo.length()) { 580 if (userInfo.substring(0, index).equalsIgnoreCase(prefix)) { 581 foundUsers.add(user); 582 } 583 } 584 } 585 } 586 587 return foundUsers; 588 } 589 } 590 | Popular Tags |