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}