Home | History | Annotate | Download | only in search
      1 /**
      2  * Copyright 2003-2007 Jive Software.
      3  *
      4  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.jivesoftware.smackx.search;
     18 
     19 import org.jivesoftware.smack.PacketCollector;
     20 import org.jivesoftware.smack.SmackConfiguration;
     21 import org.jivesoftware.smack.Connection;
     22 import org.jivesoftware.smack.XMPPException;
     23 import org.jivesoftware.smack.filter.PacketIDFilter;
     24 import org.jivesoftware.smack.packet.IQ;
     25 import org.jivesoftware.smack.provider.IQProvider;
     26 import org.jivesoftware.smack.util.PacketParserUtils;
     27 import org.jivesoftware.smackx.Form;
     28 import org.jivesoftware.smackx.FormField;
     29 import org.jivesoftware.smackx.ReportedData;
     30 import org.jivesoftware.smackx.packet.DataForm;
     31 import org.xmlpull.v1.XmlPullParser;
     32 
     33 /**
     34  * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol
     35  * 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).
     36  * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories
     37  * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines.
     38  * <p/>
     39  * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results.
     40  *
     41  * @author Derek DeMoro
     42  */
     43 public class UserSearch extends IQ {
     44 
     45     /**
     46      * Creates a new instance of UserSearch.
     47      */
     48     public UserSearch() {
     49     }
     50 
     51     public String getChildElementXML() {
     52         StringBuilder buf = new StringBuilder();
     53         buf.append("<query xmlns=\"jabber:iq:search\">");
     54         buf.append(getExtensionsXML());
     55         buf.append("</query>");
     56         return buf.toString();
     57     }
     58 
     59     /**
     60      * Returns the form for all search fields supported by the search service.
     61      *
     62      * @param con           the current Connection.
     63      * @param searchService the search service to use. (ex. search.jivesoftware.com)
     64      * @return the search form received by the server.
     65      * @throws org.jivesoftware.smack.XMPPException
     66      *          thrown if a server error has occurred.
     67      */
     68     public Form getSearchForm(Connection con, String searchService) throws XMPPException {
     69         UserSearch search = new UserSearch();
     70         search.setType(IQ.Type.GET);
     71         search.setTo(searchService);
     72 
     73         PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID()));
     74         con.sendPacket(search);
     75 
     76         IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
     77 
     78         // Cancel the collector.
     79         collector.cancel();
     80         if (response == null) {
     81             throw new XMPPException("No response from server on status set.");
     82         }
     83         if (response.getError() != null) {
     84             throw new XMPPException(response.getError());
     85         }
     86         return Form.getFormFrom(response);
     87     }
     88 
     89     /**
     90      * Sends the filled out answer form to be sent and queried by the search service.
     91      *
     92      * @param con           the current Connection.
     93      * @param searchForm    the <code>Form</code> to send for querying.
     94      * @param searchService the search service to use. (ex. search.jivesoftware.com)
     95      * @return ReportedData the data found from the query.
     96      * @throws org.jivesoftware.smack.XMPPException
     97      *          thrown if a server error has occurred.
     98      */
     99     public ReportedData sendSearchForm(Connection con, Form searchForm, String searchService) throws XMPPException {
    100         UserSearch search = new UserSearch();
    101         search.setType(IQ.Type.SET);
    102         search.setTo(searchService);
    103         search.addExtension(searchForm.getDataFormToSend());
    104 
    105         PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID()));
    106 
    107         con.sendPacket(search);
    108 
    109         IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    110 
    111         // Cancel the collector.
    112         collector.cancel();
    113         if (response == null) {
    114             throw new XMPPException("No response from server on status set.");
    115         }
    116         if (response.getError() != null) {
    117             return sendSimpleSearchForm(con, searchForm, searchService);
    118         }
    119 
    120 
    121         return ReportedData.getReportedDataFrom(response);
    122     }
    123 
    124     /**
    125      * Sends the filled out answer form to be sent and queried by the search service.
    126      *
    127      * @param con           the current Connection.
    128      * @param searchForm    the <code>Form</code> to send for querying.
    129      * @param searchService the search service to use. (ex. search.jivesoftware.com)
    130      * @return ReportedData the data found from the query.
    131      * @throws org.jivesoftware.smack.XMPPException
    132      *          thrown if a server error has occurred.
    133      */
    134     public ReportedData sendSimpleSearchForm(Connection con, Form searchForm, String searchService) throws XMPPException {
    135         SimpleUserSearch search = new SimpleUserSearch();
    136         search.setForm(searchForm);
    137         search.setType(IQ.Type.SET);
    138         search.setTo(searchService);
    139 
    140         PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID()));
    141 
    142         con.sendPacket(search);
    143 
    144         IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    145 
    146         // Cancel the collector.
    147         collector.cancel();
    148         if (response == null) {
    149             throw new XMPPException("No response from server on status set.");
    150         }
    151         if (response.getError() != null) {
    152             throw new XMPPException(response.getError());
    153         }
    154 
    155         if (response instanceof SimpleUserSearch) {
    156             return ((SimpleUserSearch) response).getReportedData();
    157         }
    158         return null;
    159     }
    160 
    161     /**
    162      * Internal Search service Provider.
    163      */
    164     public static class Provider implements IQProvider {
    165 
    166         /**
    167          * Provider Constructor.
    168          */
    169         public Provider() {
    170             super();
    171         }
    172 
    173         public IQ parseIQ(XmlPullParser parser) throws Exception {
    174             UserSearch search = null;
    175             SimpleUserSearch simpleUserSearch = new SimpleUserSearch();
    176 
    177             boolean done = false;
    178             while (!done) {
    179                 int eventType = parser.next();
    180                 if (eventType == XmlPullParser.START_TAG && parser.getName().equals("instructions")) {
    181                     buildDataForm(simpleUserSearch, parser.nextText(), parser);
    182                     return simpleUserSearch;
    183                 }
    184                 else if (eventType == XmlPullParser.START_TAG && parser.getName().equals("item")) {
    185                     simpleUserSearch.parseItems(parser);
    186                     return simpleUserSearch;
    187                 }
    188                 else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) {
    189                     // Otherwise, it must be a packet extension.
    190                     search = new UserSearch();
    191                     search.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(),
    192                             parser.getNamespace(), parser));
    193 
    194                 }
    195                 else if (eventType == XmlPullParser.END_TAG) {
    196                     if (parser.getName().equals("query")) {
    197                         done = true;
    198                     }
    199                 }
    200             }
    201 
    202             if (search != null) {
    203                 return search;
    204             }
    205             return simpleUserSearch;
    206         }
    207     }
    208 
    209     private static void buildDataForm(SimpleUserSearch search, String instructions, XmlPullParser parser) throws Exception {
    210         DataForm dataForm = new DataForm(Form.TYPE_FORM);
    211         boolean done = false;
    212         dataForm.setTitle("User Search");
    213         dataForm.addInstruction(instructions);
    214         while (!done) {
    215             int eventType = parser.next();
    216 
    217             if (eventType == XmlPullParser.START_TAG && !parser.getNamespace().equals("jabber:x:data")) {
    218                 String name = parser.getName();
    219                 FormField field = new FormField(name);
    220 
    221                 // Handle hard coded values.
    222                 if(name.equals("first")){
    223                     field.setLabel("First Name");
    224                 }
    225                 else if(name.equals("last")){
    226                     field.setLabel("Last Name");
    227                 }
    228                 else if(name.equals("email")){
    229                     field.setLabel("Email Address");
    230                 }
    231                 else if(name.equals("nick")){
    232                     field.setLabel("Nickname");
    233                 }
    234 
    235                 field.setType(FormField.TYPE_TEXT_SINGLE);
    236                 dataForm.addField(field);
    237             }
    238             else if (eventType == XmlPullParser.END_TAG) {
    239                 if (parser.getName().equals("query")) {
    240                     done = true;
    241                 }
    242             }
    243             else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) {
    244                 search.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(),
    245                         parser.getNamespace(), parser));
    246                 done = true;
    247             }
    248         }
    249         if (search.getExtension("x", "jabber:x:data") == null) {
    250             search.addExtension(dataForm);
    251         }
    252     }
    253 
    254 
    255 }
    256