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; 027import org.jivesoftware.smackx.xdata.Form; 028import org.jivesoftware.smackx.xdata.FormField; 029import org.jivesoftware.smackx.xdata.packet.DataForm; 030import org.jxmpp.jid.DomainBareJid; 031import org.xmlpull.v1.XmlPullParser; 032 033/** 034 * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol 035 * 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). 036 * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories 037 * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines. 038 * <p/> 039 * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results. 040 * 041 * @author Derek DeMoro 042 */ 043public class UserSearch extends SimpleIQ { 044 045 public static final String ELEMENT = QUERY_ELEMENT; 046 public static final String NAMESPACE = "jabber:iq:search"; 047 048 /** 049 * Creates a new instance of UserSearch. 050 */ 051 public UserSearch() { 052 super(ELEMENT, NAMESPACE); 053 } 054 055 /** 056 * Returns the form for all search fields supported by the search service. 057 * 058 * @param con the current XMPPConnection. 059 * @param searchService the search service to use. (ex. search.jivesoftware.com) 060 * @return the search form received by the server. 061 * @throws XMPPErrorException 062 * @throws NoResponseException 063 * @throws NotConnectedException 064 * @throws InterruptedException 065 */ 066 public Form getSearchForm(XMPPConnection con, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 067 UserSearch search = new UserSearch(); 068 search.setType(IQ.Type.get); 069 search.setTo(searchService); 070 071 IQ response = (IQ) con.createStanzaCollectorAndSend(search).nextResultOrThrow(); 072 return Form.getFormFrom(response); 073 } 074 075 /** 076 * Sends the filled out answer form to be sent and queried by the search service. 077 * 078 * @param con the current XMPPConnection. 079 * @param searchForm the <code>Form</code> to send for querying. 080 * @param searchService the search service to use. (ex. search.jivesoftware.com) 081 * @return ReportedData the data found from the query. 082 * @throws XMPPErrorException 083 * @throws NoResponseException 084 * @throws NotConnectedException 085 * @throws InterruptedException 086 */ 087 public ReportedData sendSearchForm(XMPPConnection con, Form searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 088 UserSearch search = new UserSearch(); 089 search.setType(IQ.Type.set); 090 search.setTo(searchService); 091 search.addExtension(searchForm.getDataFormToSend()); 092 093 IQ response = (IQ) con.createStanzaCollectorAndSend(search).nextResultOrThrow(); 094 return ReportedData.getReportedDataFrom(response); 095 } 096 097 /** 098 * Sends the filled out answer form to be sent and queried by the search service. 099 * 100 * @param con the current XMPPConnection. 101 * @param searchForm the <code>Form</code> to send for querying. 102 * @param searchService the search service to use. (ex. search.jivesoftware.com) 103 * @return ReportedData the data found from the query. 104 * @throws XMPPErrorException 105 * @throws NoResponseException 106 * @throws NotConnectedException 107 * @throws InterruptedException 108 */ 109 public ReportedData sendSimpleSearchForm(XMPPConnection con, Form searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 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.createStanzaCollectorAndSend(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 Exception { 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 Exception { 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}