1 /** 2 * Copyright 2003-2007 Jive Software. 3 * 4 * All rights reserved. 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.smackx.search; 18 19 import org.jivesoftware.smack.PacketCollector; 20 import org.jivesoftware.smack.SmackConfiguration; 21 import org.jivesoftware.smack.Connection; 22 import org.jivesoftware.smack.XMPPException; 23 import org.jivesoftware.smack.filter.PacketIDFilter; 24 import org.jivesoftware.smack.packet.IQ; 25 import org.jivesoftware.smack.provider.IQProvider; 26 import org.jivesoftware.smack.util.PacketParserUtils; 27 import org.jivesoftware.smackx.Form; 28 import org.jivesoftware.smackx.FormField; 29 import org.jivesoftware.smackx.ReportedData; 30 import org.jivesoftware.smackx.packet.DataForm; 31 import org.xmlpull.v1.XmlPullParser; 32 33 /** 34 * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol 35 * has been used mainly to search for people who have registered with user directories (e.g., the "Jabber User Directory" hosted at users.jabber.org). 36 * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories 37 * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines. 38 * <p/> 39 * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results. 40 * 41 * @author Derek DeMoro 42 */ 43 public class UserSearch extends IQ { 44 45 /** 46 * Creates a new instance of UserSearch. 47 */ 48 public UserSearch() { 49 } 50 51 public String getChildElementXML() { 52 StringBuilder buf = new StringBuilder(); 53 buf.append("<query xmlns=\"jabber:iq:search\">"); 54 buf.append(getExtensionsXML()); 55 buf.append("</query>"); 56 return buf.toString(); 57 } 58 59 /** 60 * Returns the form for all search fields supported by the search service. 61 * 62 * @param con the current Connection. 63 * @param searchService the search service to use. (ex. search.jivesoftware.com) 64 * @return the search form received by the server. 65 * @throws org.jivesoftware.smack.XMPPException 66 * thrown if a server error has occurred. 67 */ 68 public Form getSearchForm(Connection con, String searchService) throws XMPPException { 69 UserSearch search = new UserSearch(); 70 search.setType(IQ.Type.GET); 71 search.setTo(searchService); 72 73 PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID())); 74 con.sendPacket(search); 75 76 IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); 77 78 // Cancel the collector. 79 collector.cancel(); 80 if (response == null) { 81 throw new XMPPException("No response from server on status set."); 82 } 83 if (response.getError() != null) { 84 throw new XMPPException(response.getError()); 85 } 86 return Form.getFormFrom(response); 87 } 88 89 /** 90 * Sends the filled out answer form to be sent and queried by the search service. 91 * 92 * @param con the current Connection. 93 * @param searchForm the <code>Form</code> to send for querying. 94 * @param searchService the search service to use. (ex. search.jivesoftware.com) 95 * @return ReportedData the data found from the query. 96 * @throws org.jivesoftware.smack.XMPPException 97 * thrown if a server error has occurred. 98 */ 99 public ReportedData sendSearchForm(Connection con, Form searchForm, String searchService) throws XMPPException { 100 UserSearch search = new UserSearch(); 101 search.setType(IQ.Type.SET); 102 search.setTo(searchService); 103 search.addExtension(searchForm.getDataFormToSend()); 104 105 PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID())); 106 107 con.sendPacket(search); 108 109 IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); 110 111 // Cancel the collector. 112 collector.cancel(); 113 if (response == null) { 114 throw new XMPPException("No response from server on status set."); 115 } 116 if (response.getError() != null) { 117 return sendSimpleSearchForm(con, searchForm, searchService); 118 } 119 120 121 return ReportedData.getReportedDataFrom(response); 122 } 123 124 /** 125 * Sends the filled out answer form to be sent and queried by the search service. 126 * 127 * @param con the current Connection. 128 * @param searchForm the <code>Form</code> to send for querying. 129 * @param searchService the search service to use. (ex. search.jivesoftware.com) 130 * @return ReportedData the data found from the query. 131 * @throws org.jivesoftware.smack.XMPPException 132 * thrown if a server error has occurred. 133 */ 134 public ReportedData sendSimpleSearchForm(Connection con, Form searchForm, String searchService) throws XMPPException { 135 SimpleUserSearch search = new SimpleUserSearch(); 136 search.setForm(searchForm); 137 search.setType(IQ.Type.SET); 138 search.setTo(searchService); 139 140 PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID())); 141 142 con.sendPacket(search); 143 144 IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); 145 146 // Cancel the collector. 147 collector.cancel(); 148 if (response == null) { 149 throw new XMPPException("No response from server on status set."); 150 } 151 if (response.getError() != null) { 152 throw new XMPPException(response.getError()); 153 } 154 155 if (response instanceof SimpleUserSearch) { 156 return ((SimpleUserSearch) response).getReportedData(); 157 } 158 return null; 159 } 160 161 /** 162 * Internal Search service Provider. 163 */ 164 public static class Provider implements IQProvider { 165 166 /** 167 * Provider Constructor. 168 */ 169 public Provider() { 170 super(); 171 } 172 173 public IQ parseIQ(XmlPullParser parser) throws Exception { 174 UserSearch search = null; 175 SimpleUserSearch simpleUserSearch = new SimpleUserSearch(); 176 177 boolean done = false; 178 while (!done) { 179 int eventType = parser.next(); 180 if (eventType == XmlPullParser.START_TAG && parser.getName().equals("instructions")) { 181 buildDataForm(simpleUserSearch, parser.nextText(), parser); 182 return simpleUserSearch; 183 } 184 else if (eventType == XmlPullParser.START_TAG && parser.getName().equals("item")) { 185 simpleUserSearch.parseItems(parser); 186 return simpleUserSearch; 187 } 188 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) { 189 // Otherwise, it must be a packet extension. 190 search = new UserSearch(); 191 search.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(), 192 parser.getNamespace(), parser)); 193 194 } 195 else if (eventType == XmlPullParser.END_TAG) { 196 if (parser.getName().equals("query")) { 197 done = true; 198 } 199 } 200 } 201 202 if (search != null) { 203 return search; 204 } 205 return simpleUserSearch; 206 } 207 } 208 209 private static void buildDataForm(SimpleUserSearch search, String instructions, XmlPullParser parser) throws Exception { 210 DataForm dataForm = new DataForm(Form.TYPE_FORM); 211 boolean done = false; 212 dataForm.setTitle("User Search"); 213 dataForm.addInstruction(instructions); 214 while (!done) { 215 int eventType = parser.next(); 216 217 if (eventType == XmlPullParser.START_TAG && !parser.getNamespace().equals("jabber:x:data")) { 218 String name = parser.getName(); 219 FormField field = new FormField(name); 220 221 // Handle hard coded values. 222 if(name.equals("first")){ 223 field.setLabel("First Name"); 224 } 225 else if(name.equals("last")){ 226 field.setLabel("Last Name"); 227 } 228 else if(name.equals("email")){ 229 field.setLabel("Email Address"); 230 } 231 else if(name.equals("nick")){ 232 field.setLabel("Nickname"); 233 } 234 235 field.setType(FormField.TYPE_TEXT_SINGLE); 236 dataForm.addField(field); 237 } 238 else if (eventType == XmlPullParser.END_TAG) { 239 if (parser.getName().equals("query")) { 240 done = true; 241 } 242 } 243 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) { 244 search.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(), 245 parser.getNamespace(), parser)); 246 done = true; 247 } 248 } 249 if (search.getExtension("x", "jabber:x:data") == null) { 250 search.addExtension(dataForm); 251 } 252 } 253 254 255 } 256