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}