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 org.jivesoftware.smack.SmackException.NoResponseException; 020import org.jivesoftware.smack.SmackException.NotConnectedException; 021import org.jivesoftware.smack.XMPPConnection; 022import org.jivesoftware.smack.XMPPException.XMPPErrorException; 023import org.jivesoftware.smack.packet.IQ; 024import org.jivesoftware.smack.packet.SimpleIQ; 025import org.jivesoftware.smack.provider.IQProvider; 026import org.jivesoftware.smack.util.PacketParserUtils; 027 028import org.jivesoftware.smackx.xdata.Form; 029import org.jivesoftware.smackx.xdata.FormField; 030import org.jivesoftware.smackx.xdata.packet.DataForm; 031 032import org.jxmpp.jid.DomainBareJid; 033import org.xmlpull.v1.XmlPullParser; 034 035/** 036 * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol 037 * 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). 038 * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories 039 * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines. 040 * 041 * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results. 042 * 043 * @author Derek DeMoro 044 */ 045public class UserSearch extends SimpleIQ { 046 047 public static final String ELEMENT = QUERY_ELEMENT; 048 public static final String NAMESPACE = "jabber:iq:search"; 049 050 /** 051 * Creates a new instance of UserSearch. 052 */ 053 public UserSearch() { 054 super(ELEMENT, NAMESPACE); 055 } 056 057 /** 058 * Returns the form for all search fields supported by the search service. 059 * 060 * @param con the current XMPPConnection. 061 * @param searchService the search service to use. (ex. search.jivesoftware.com) 062 * @return the search form received by the server. 063 * @throws XMPPErrorException 064 * @throws NoResponseException 065 * @throws NotConnectedException 066 * @throws InterruptedException 067 */ 068 public Form getSearchForm(XMPPConnection con, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 069 UserSearch search = new UserSearch(); 070 search.setType(IQ.Type.get); 071 search.setTo(searchService); 072 073 IQ response = con.createStanzaCollectorAndSend(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 * @throws InterruptedException 088 */ 089 public ReportedData sendSearchForm(XMPPConnection con, Form searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 090 UserSearch search = new UserSearch(); 091 search.setType(IQ.Type.set); 092 search.setTo(searchService); 093 search.addExtension(searchForm.getDataFormToSend()); 094 095 IQ response = con.createStanzaCollectorAndSend(search).nextResultOrThrow(); 096 return ReportedData.getReportedDataFrom(response); 097 } 098 099 /** 100 * Sends the filled out answer form to be sent and queried by the search service. 101 * 102 * @param con the current XMPPConnection. 103 * @param searchForm the <code>Form</code> to send for querying. 104 * @param searchService the search service to use. (ex. search.jivesoftware.com) 105 * @return ReportedData the data found from the query. 106 * @throws XMPPErrorException 107 * @throws NoResponseException 108 * @throws NotConnectedException 109 * @throws InterruptedException 110 */ 111 public ReportedData sendSimpleSearchForm(XMPPConnection con, Form searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 112 SimpleUserSearch search = new SimpleUserSearch(); 113 search.setForm(searchForm); 114 search.setType(IQ.Type.set); 115 search.setTo(searchService); 116 117 SimpleUserSearch response = con.createStanzaCollectorAndSend(search).nextResultOrThrow(); 118 return response.getReportedData(); 119 } 120 121 /** 122 * Internal Search service Provider. 123 */ 124 public static class Provider extends IQProvider<IQ> { 125 126 // FIXME this provider does return two different types of IQs 127 @Override 128 public IQ parse(XmlPullParser parser, int initialDepth) throws Exception { 129 UserSearch search = null; 130 SimpleUserSearch simpleUserSearch = new SimpleUserSearch(); 131 132 boolean done = false; 133 while (!done) { 134 int eventType = parser.next(); 135 if (eventType == XmlPullParser.START_TAG && parser.getName().equals("instructions")) { 136 buildDataForm(simpleUserSearch, parser.nextText(), parser); 137 return simpleUserSearch; 138 } 139 else if (eventType == XmlPullParser.START_TAG && parser.getName().equals("item")) { 140 simpleUserSearch.parseItems(parser); 141 return simpleUserSearch; 142 } 143 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) { 144 // Otherwise, it must be a packet extension. 145 search = new UserSearch(); 146 PacketParserUtils.addExtensionElement(search, parser); 147 } 148 else if (eventType == XmlPullParser.END_TAG) { 149 if (parser.getName().equals("query")) { 150 done = true; 151 } 152 } 153 } 154 155 if (search != null) { 156 return search; 157 } 158 return simpleUserSearch; 159 } 160 } 161 162 private static void buildDataForm(SimpleUserSearch search, 163 String instructions, XmlPullParser parser) 164 throws Exception { 165 DataForm dataForm = new DataForm(DataForm.Type.form); 166 boolean done = false; 167 dataForm.setTitle("User Search"); 168 dataForm.addInstruction(instructions); 169 while (!done) { 170 int eventType = parser.next(); 171 172 if (eventType == XmlPullParser.START_TAG && !parser.getNamespace().equals("jabber:x:data")) { 173 String name = parser.getName(); 174 FormField field = new FormField(name); 175 176 // Handle hard coded values. 177 if (name.equals("first")) { 178 field.setLabel("First Name"); 179 } 180 else if (name.equals("last")) { 181 field.setLabel("Last Name"); 182 } 183 else if (name.equals("email")) { 184 field.setLabel("Email Address"); 185 } 186 else if (name.equals("nick")) { 187 field.setLabel("Nickname"); 188 } 189 190 field.setType(FormField.Type.text_single); 191 dataForm.addField(field); 192 } 193 else if (eventType == XmlPullParser.END_TAG) { 194 if (parser.getName().equals("query")) { 195 done = true; 196 } 197 } 198 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) { 199 PacketParserUtils.addExtensionElement(search, parser); 200 done = true; 201 } 202 } 203 if (search.getExtension("x", "jabber:x:data") == null) { 204 search.addExtension(dataForm); 205 } 206 } 207 208 209}