001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.search; 018 019import java.io.IOException; 020 021import org.jivesoftware.smack.SmackException; 022import org.jivesoftware.smack.SmackException.NoResponseException; 023import org.jivesoftware.smack.SmackException.NotConnectedException; 024import org.jivesoftware.smack.XMPPConnection; 025import org.jivesoftware.smack.XMPPException.XMPPErrorException; 026import org.jivesoftware.smack.packet.IQ; 027import org.jivesoftware.smack.packet.SimpleIQ; 028import org.jivesoftware.smack.provider.IQProvider; 029import org.jivesoftware.smack.util.PacketParserUtils; 030import org.jivesoftware.smackx.xdata.Form; 031import org.jivesoftware.smackx.xdata.FormField; 032import org.jivesoftware.smackx.xdata.packet.DataForm; 033import org.xmlpull.v1.XmlPullParser; 034import org.xmlpull.v1.XmlPullParserException; 035 036/** 037 * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol 038 * 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). 039 * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories 040 * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines. 041 * <p/> 042 * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results. 043 * 044 * @author Derek DeMoro 045 */ 046public class UserSearch extends SimpleIQ { 047 048 public static final String ELEMENT = QUERY_ELEMENT; 049 public static final String NAMESPACE = "jabber:iq:search"; 050 051 /** 052 * Creates a new instance of UserSearch. 053 */ 054 public UserSearch() { 055 super(ELEMENT, NAMESPACE); 056 } 057 058 /** 059 * Returns the form for all search fields supported by the search service. 060 * 061 * @param con the current XMPPConnection. 062 * @param searchService the search service to use. (ex. search.jivesoftware.com) 063 * @return the search form received by the server. 064 * @throws XMPPErrorException 065 * @throws NoResponseException 066 * @throws NotConnectedException 067 */ 068 public Form getSearchForm(XMPPConnection con, String searchService) throws NoResponseException, XMPPErrorException, NotConnectedException { 069 UserSearch search = new UserSearch(); 070 search.setType(IQ.Type.get); 071 search.setTo(searchService); 072 073 IQ response = (IQ) con.createPacketCollectorAndSend(search).nextResultOrThrow(); 074 return Form.getFormFrom(response); 075 } 076 077 /** 078 * Sends the filled out answer form to be sent and queried by the search service. 079 * 080 * @param con the current XMPPConnection. 081 * @param searchForm the <code>Form</code> to send for querying. 082 * @param searchService the search service to use. (ex. search.jivesoftware.com) 083 * @return ReportedData the data found from the query. 084 * @throws XMPPErrorException 085 * @throws NoResponseException 086 * @throws NotConnectedException 087 */ 088 public ReportedData sendSearchForm(XMPPConnection con, Form searchForm, String searchService) throws NoResponseException, XMPPErrorException, NotConnectedException { 089 UserSearch search = new UserSearch(); 090 search.setType(IQ.Type.set); 091 search.setTo(searchService); 092 search.addExtension(searchForm.getDataFormToSend()); 093 094 IQ response = (IQ) con.createPacketCollectorAndSend(search).nextResultOrThrow(); 095 return ReportedData.getReportedDataFrom(response); 096 } 097 098 /** 099 * Sends the filled out answer form to be sent and queried by the search service. 100 * 101 * @param con the current XMPPConnection. 102 * @param searchForm the <code>Form</code> to send for querying. 103 * @param searchService the search service to use. (ex. search.jivesoftware.com) 104 * @return ReportedData the data found from the query. 105 * @throws XMPPErrorException 106 * @throws NoResponseException 107 * @throws NotConnectedException 108 */ 109 public ReportedData sendSimpleSearchForm(XMPPConnection con, Form searchForm, String searchService) throws NoResponseException, XMPPErrorException, NotConnectedException { 110 SimpleUserSearch search = new SimpleUserSearch(); 111 search.setForm(searchForm); 112 search.setType(IQ.Type.set); 113 search.setTo(searchService); 114 115 SimpleUserSearch response = (SimpleUserSearch) con.createPacketCollectorAndSend(search).nextResultOrThrow(); 116 return response.getReportedData(); 117 } 118 119 /** 120 * Internal Search service Provider. 121 */ 122 public static class Provider extends IQProvider<IQ> { 123 124 // FIXME this provider does return two different types of IQs 125 @Override 126 public IQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException { 127 UserSearch search = null; 128 SimpleUserSearch simpleUserSearch = new SimpleUserSearch(); 129 130 boolean done = false; 131 while (!done) { 132 int eventType = parser.next(); 133 if (eventType == XmlPullParser.START_TAG && parser.getName().equals("instructions")) { 134 buildDataForm(simpleUserSearch, parser.nextText(), parser); 135 return simpleUserSearch; 136 } 137 else if (eventType == XmlPullParser.START_TAG && parser.getName().equals("item")) { 138 simpleUserSearch.parseItems(parser); 139 return simpleUserSearch; 140 } 141 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) { 142 // Otherwise, it must be a packet extension. 143 search = new UserSearch(); 144 PacketParserUtils.addExtensionElement(search, parser); 145 } 146 else if (eventType == XmlPullParser.END_TAG) { 147 if (parser.getName().equals("query")) { 148 done = true; 149 } 150 } 151 } 152 153 if (search != null) { 154 return search; 155 } 156 return simpleUserSearch; 157 } 158 } 159 160 private static void buildDataForm(SimpleUserSearch search, 161 String instructions, XmlPullParser parser) 162 throws XmlPullParserException, IOException, SmackException { 163 DataForm dataForm = new DataForm(DataForm.Type.form); 164 boolean done = false; 165 dataForm.setTitle("User Search"); 166 dataForm.addInstruction(instructions); 167 while (!done) { 168 int eventType = parser.next(); 169 170 if (eventType == XmlPullParser.START_TAG && !parser.getNamespace().equals("jabber:x:data")) { 171 String name = parser.getName(); 172 FormField field = new FormField(name); 173 174 // Handle hard coded values. 175 if(name.equals("first")){ 176 field.setLabel("First Name"); 177 } 178 else if(name.equals("last")){ 179 field.setLabel("Last Name"); 180 } 181 else if(name.equals("email")){ 182 field.setLabel("Email Address"); 183 } 184 else if(name.equals("nick")){ 185 field.setLabel("Nickname"); 186 } 187 188 field.setType(FormField.Type.text_single); 189 dataForm.addField(field); 190 } 191 else if (eventType == XmlPullParser.END_TAG) { 192 if (parser.getName().equals("query")) { 193 done = true; 194 } 195 } 196 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) { 197 PacketParserUtils.addExtensionElement(search, parser); 198 done = true; 199 } 200 } 201 if (search.getExtension("x", "jabber:x:data") == null) { 202 search.addExtension(dataForm); 203 } 204 } 205 206 207}