Commit f03d3f64 authored by Ryan Graham's avatar Ryan Graham Committed by ryang

added basic support for non-data form searches to support the Miranda client (thanks Baron G)


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@1191 b35dd754-fafc-0310-a699-88a17e54d16e
parent db44b214
...@@ -8,8 +8,16 @@ ...@@ -8,8 +8,16 @@
package org.jivesoftware.messenger.plugin; package org.jivesoftware.messenger.plugin;
import java.io.File; import java.io.File;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.QName; import org.dom4j.QName;
...@@ -38,8 +46,10 @@ import org.xmpp.packet.Packet; ...@@ -38,8 +46,10 @@ import org.xmpp.packet.Packet;
* *
* The basic functionality is to query an information repository * The basic functionality is to query an information repository
* regarding the possible search fields, to send a search query, * regarding the possible search fields, to send a search query,
* and to receive search results. This implementation below uses the * and to receive search results. This implementation below primarily uses
* <a href="http://www.jabber.org/jeps/jep-0004.html">Data Forms</a>. * <a href="http://www.jabber.org/jeps/jep-0004.html">Data Forms</a>, but
* limited support for non-datforms searches has been added to support the
* Miranda client.
* <p/> * <p/>
* *
* @author Ryan Graham * @author Ryan Graham
...@@ -51,6 +61,10 @@ public class SearchPlugin implements Component, Plugin { ...@@ -51,6 +61,10 @@ public class SearchPlugin implements Component, Plugin {
private PluginManager pluginManager; private PluginManager pluginManager;
private static final String SERVICE_NAME = "search"; private static final String SERVICE_NAME = "search";
private static String serverName;
private static String instructions = "The following fields are available for search. "
+ "Wildcard (*) characters are allowed as part the of query.";
private static Element probeResult; private static Element probeResult;
...@@ -58,6 +72,7 @@ public class SearchPlugin implements Component, Plugin { ...@@ -58,6 +72,7 @@ public class SearchPlugin implements Component, Plugin {
public SearchPlugin() { public SearchPlugin() {
server = XMPPServer.getInstance(); server = XMPPServer.getInstance();
serverName = server.getServerInfo().getName();
// See if the installed provider supports searching. If not, workaround // See if the installed provider supports searching. If not, workaround
// by providing our own searching. // by providing our own searching.
UserManager manager = UserManager.getInstance(); UserManager manager = UserManager.getInstance();
...@@ -101,18 +116,23 @@ public class SearchPlugin implements Component, Plugin { ...@@ -101,18 +116,23 @@ public class SearchPlugin implements Component, Plugin {
if (probeResult == null) { if (probeResult == null) {
probeResult = DocumentHelper.createElement(QName.get("query", "jabber:iq:search")); probeResult = DocumentHelper.createElement(QName.get("query", "jabber:iq:search"));
//non-data form
probeResult.addElement("instructions").addText(instructions);
XDataFormImpl searchForm = new XDataFormImpl(DataForm.TYPE_FORM); XDataFormImpl searchForm = new XDataFormImpl(DataForm.TYPE_FORM);
searchForm.setTitle("User Search"); searchForm.setTitle("User Search");
searchForm.addInstruction("The following fields are available for search. " searchForm.addInstruction(instructions);
+ "Wildcard (*) characters are allowed as part the of query.");
Iterator iter = searchFields.iterator(); Iterator iter = searchFields.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
String searchField = (String) iter.next(); String searchField = (String) iter.next();
//non-data form
probeResult.addElement(searchField.toLowerCase());
XFormFieldImpl field = new XFormFieldImpl(searchField); XFormFieldImpl field = new XFormFieldImpl(searchField);
field.setType(FormField.TYPE_TEXT_SINGLE); field.setType(FormField.TYPE_TEXT_SINGLE);
field.setLabel(initCap(searchField)); field.setLabel(searchField);
field.setRequired(false); field.setRequired(false);
searchForm.addField(field); searchForm.addField(field);
} }
...@@ -215,42 +235,26 @@ public class SearchPlugin implements Component, Plugin { ...@@ -215,42 +235,26 @@ public class SearchPlugin implements Component, Plugin {
} }
private IQ processSetPacket(IQ packet) { private IQ processSetPacket(IQ packet) {
XDataFormImpl searchResults = new XDataFormImpl(DataForm.TYPE_RESULT);
XFormFieldImpl field = new XFormFieldImpl("jid");
field.setLabel("JID");
searchResults.addReportedField(field);
for (String fieldName: searchFields) {
field = new XFormFieldImpl(fieldName);
field.setLabel(initCap(fieldName));
searchResults.addReportedField(field);
}
List<User> users = new ArrayList<User>(); List<User> users = new ArrayList<User>();
Element incomingForm = packet.getChildElement(); Element incomingForm = packet.getChildElement();
Element form = incomingForm.element(QName.get("x", "jabber:x:data")); boolean isDataFormQuery = (incomingForm.element(QName.get("x", "jabber:x:data")) != null);
Iterator fields = form.elementIterator("field");
while (fields.hasNext()) {
Element searchField = (Element) fields.next();
Iterator iter = searchField.elementIterator("value"); Hashtable<String, String> searchList = extractSearchQuery(incomingForm);
while (iter.hasNext()) { Enumeration<String> searchIter = searchList.keys();
Element queryField = (Element) iter.next(); while (searchIter.hasMoreElements()) {
String field = (String) searchIter.nextElement();
String query = (String) searchList.get(field);
Iterator foundIter = null; Iterator foundIter = null;
if (userManager != null) { if (userManager != null) {
String query = queryField.getTextTrim(); if (query.length() > 0 && !query.equals("jabber:iq:search")) {
//psi returns every field even if it is empty
if (query.length() > 0) {
foundIter = userManager.findUsers(new HashSet<String>( foundIter = userManager.findUsers(new HashSet<String>(
Arrays.asList(searchField.attributeValue("var"))), query).iterator(); Arrays.asList((field))), query).iterator();
} }
} }
else { else {
foundIter = findUsers(searchField.attributeValue("var"), foundIter = findUsers(field, query).iterator();
queryField.getTextTrim()).iterator();
} }
// Filter out all duplicate users. // Filter out all duplicate users.
...@@ -263,6 +267,88 @@ public class SearchPlugin implements Component, Plugin { ...@@ -263,6 +267,88 @@ public class SearchPlugin implements Component, Plugin {
} }
} }
} }
if (isDataFormQuery) {
return replyDataFormResult(users, packet);
} else {
return replyNonDataFormResult(users, packet);
}
}
/**
* nick, first, last, email fields have been hardcoded to support Miranda which does not
* follow the JEP-0055 spec by requesting a list of searchable fields. If/when Miranda
* is updated to follow the spect the hardcoded field checks can be removed and replaced
* with the commented code below.
*/
private Hashtable<String, String> extractSearchQuery(Element incomingForm) {
Hashtable<String, String> searchList = new Hashtable<String, String>();
Element form = incomingForm.element(QName.get("x", "jabber:x:data"));
if (form == null) {
Element name = incomingForm.element("name");
if (name != null) {
searchList.put("Name", name.getTextTrim());
}
Element nick = incomingForm.element("nick");
if (nick != null) {
searchList.put("Username", nick.getTextTrim());
}
Element first = incomingForm.element("first");
if (first != null) {
searchList.put("Name", first.getTextTrim());
}
Element last = incomingForm.element("last");
if (last != null) {
searchList.put("Name", last.getTextTrim());
}
Element email = incomingForm.element("email");
if (email != null) {
searchList.put("Email", email.getTextTrim());
}
/*
Iterator iter = searchFields.iterator();
while (iter.hasNext()) {
String field = (String) iter.next();
Element e = incomingForm.element(field);
if (field != null) {
searchList.put(field, e.getTextTrim());
}
}
*/
}
else {
Iterator fields = form.elementIterator("field");
while (fields.hasNext()) {
Element searchField = (Element) fields.next();
Iterator iter = searchField.elementIterator("value");
while (iter.hasNext()) {
Element queryField = (Element) iter.next();
String query = queryField.getTextTrim();
searchList.put(searchField.attributeValue("var"), query);
}
}
}
return searchList;
}
private IQ replyDataFormResult(List users, IQ packet) {
XDataFormImpl searchResults = new XDataFormImpl(DataForm.TYPE_RESULT);
XFormFieldImpl field = new XFormFieldImpl("jid");
field.setLabel("JID");
searchResults.addReportedField(field);
for (String fieldName : searchFields) {
field = new XFormFieldImpl(fieldName);
field.setLabel(fieldName);
searchResults.addReportedField(field);
} }
Iterator userIter = users.iterator(); Iterator userIter = users.iterator();
...@@ -273,7 +359,7 @@ public class SearchPlugin implements Component, Plugin { ...@@ -273,7 +359,7 @@ public class SearchPlugin implements Component, Plugin {
ArrayList<XFormFieldImpl> items = new ArrayList<XFormFieldImpl>(); ArrayList<XFormFieldImpl> items = new ArrayList<XFormFieldImpl>();
XFormFieldImpl fieldJID = new XFormFieldImpl("jid"); XFormFieldImpl fieldJID = new XFormFieldImpl("jid");
fieldJID.addValue(username + "@" + server.getServerInfo().getName()); fieldJID.addValue(username + "@" + serverName);
items.add(fieldJID); items.add(fieldJID);
XFormFieldImpl fieldUsername = new XFormFieldImpl("Username"); XFormFieldImpl fieldUsername = new XFormFieldImpl("Username");
...@@ -300,21 +386,28 @@ public class SearchPlugin implements Component, Plugin { ...@@ -300,21 +386,28 @@ public class SearchPlugin implements Component, Plugin {
return replyPacket; return replyPacket;
} }
private static String initCap(String s) { private IQ replyNonDataFormResult(List users, IQ packet) {
if (s == null) { Element replyQuery = DocumentHelper.createElement(QName.get("query", "jabber:iq:search"));
return null; String serverName = XMPPServer.getInstance().getServerInfo().getName();
} Iterator userIter = users.iterator();
while (userIter.hasNext()) {
User user = (User) userIter.next();
StringTokenizer st = new StringTokenizer(s); Element item = DocumentHelper.createElement("item");
StringBuffer sb = new StringBuffer(); Attribute jib = DocumentHelper.createAttribute(item, "jid", user.getUsername() + "@" + serverName);
while (st.hasMoreTokens()) { item.add(jib);
String t = st.nextToken(); Element nick = DocumentHelper.createElement("nick");
String first = t.substring(0, 1).toUpperCase(); nick.addText(user.getName());
String rest = t.substring(1).toLowerCase(); item.add(nick);
sb.append(first + rest); Element email = DocumentHelper.createElement("email");
email.addText(user.getEmail());
item.add(email);
replyQuery.add(item);
} }
IQ replyPacket = IQ.createResultIQ(packet);
replyPacket.setChildElement(replyQuery);
return sb.toString().trim(); return replyPacket;
} }
/** /**
...@@ -352,16 +445,15 @@ public class SearchPlugin implements Component, Plugin { ...@@ -352,16 +445,15 @@ public class SearchPlugin implements Component, Plugin {
try { try {
foundUsers.add(userManager.getUser(query)); foundUsers.add(userManager.getUser(query));
return foundUsers; return foundUsers;
} catch (UserNotFoundException e) { }
catch (UserNotFoundException e) {
Log.error("Error getting user", e); Log.error("Error getting user", e);
} }
} }
else if (field.equals("Name")) { else if (field.equals("Name")) {
if (query.equalsIgnoreCase(user.getName())) { if (query.equalsIgnoreCase(user.getName())) {
foundUsers.add(user); foundUsers.add(user);
} }
} }
else if (field.equals("Email")) { else if (field.equals("Email")) {
if (user.getEmail() != null) { if (user.getEmail() != null) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment