001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.dsml.protocol;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.IOException;
033    import java.util.ArrayList;
034    import java.util.LinkedHashSet;
035    import java.util.LinkedList;
036    import java.util.List;
037    import javax.xml.bind.JAXBElement;
038    import org.opends.server.protocols.asn1.ASN1Exception;
039    import org.opends.server.protocols.asn1.ASN1OctetString;
040    import org.opends.server.protocols.ldap.LDAPAttribute;
041    import org.opends.server.protocols.ldap.LDAPConstants;
042    import org.opends.server.protocols.ldap.LDAPFilter;
043    import org.opends.server.protocols.ldap.LDAPMessage;
044    import org.opends.server.protocols.ldap.SearchRequestProtocolOp;
045    import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
046    import org.opends.server.protocols.ldap.SearchResultReferenceProtocolOp;
047    import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp;
048    import org.opends.server.tools.LDAPConnection;
049    import org.opends.server.types.ByteString;
050    import org.opends.server.types.DereferencePolicy;
051    import org.opends.server.types.LDAPException;
052    import org.opends.server.types.RawFilter;
053    import org.opends.server.types.SearchScope;
054    
055    
056    
057    /**
058     * This class provides the functionality for the performing an
059     * LDAP SEARCH operation based on the specified DSML request.
060     */
061    public class DSMLSearchOperation
062    {
063      private LDAPConnection connection;
064    
065      /**
066       * Create the instance with the specified connection.
067       *
068       * @param connection    The LDAP connection to send the request on.
069       */
070    
071      public DSMLSearchOperation(LDAPConnection connection)
072      {
073        this.connection = connection;
074      }
075    
076      /**
077       * Returns a new AND search filter with the provided filter components.
078       *
079       * @param filterSet The filter components for this filter
080       *
081       * @return a new AND search filter with the provided filter components.
082       *
083       * @throws LDAPException an LDAPException is thrown if the creation of a
084       *                       filter component fails.
085       */
086      private static LDAPFilter createANDFilter(FilterSet filterSet)
087              throws LDAPException {
088        List<JAXBElement<?>> list = filterSet.getFilterGroup();
089        ArrayList<RawFilter> filters = new ArrayList<RawFilter>(list.size());
090    
091        for(JAXBElement<?> filter : list) {
092          filters.add(createFilter(filter));
093        }
094        return LDAPFilter.createANDFilter(filters);
095      }
096    
097      /**
098       * Returns a new Approximate search filter with the provided information.
099       *
100       * @param ava the attribute value assertion for this approximate filter.
101       *
102       * @return a new Approximate search filter with the provided information.
103       */
104      private static LDAPFilter createApproximateFilter(AttributeValueAssertion ava)
105      {
106        return LDAPFilter.createApproximateFilter(ava.getName(),
107                                               new ASN1OctetString(ava.getValue()));
108      }
109    
110      /**
111       * Returns a new Equality search filter with the provided information.
112       *
113       * @param ava the attribute value assertion for this Equality filter.
114       *
115       * @return a new Equality search filter with the provided information.
116       */
117      private static LDAPFilter createEqualityFilter(AttributeValueAssertion ava) {
118        return LDAPFilter.createEqualityFilter(ava.getName(),
119                                              new ASN1OctetString(ava.getValue()));
120      }
121    
122      /**
123       * Returns a new Extensible search filter with the provided information.
124       *
125       * @param mra the matching rule assertion for this Extensible filter.
126       *
127       * @return a new Extensible search filter with the provided information.
128       */
129      private static LDAPFilter createExtensibleFilter(MatchingRuleAssertion mra) {
130        return LDAPFilter.createExtensibleFilter(mra.getMatchingRule(),
131                                            mra.getName(),
132                                            new ASN1OctetString(mra.getValue()),
133                                            mra.isDnAttributes());
134      }
135    
136      /**
137       * Returns a new GreaterOrEqual search filter with the provided information.
138       *
139       * @param ava the attribute value assertion for this GreaterOrEqual filter.
140       *
141       * @return a new GreaterOrEqual search filter with the provided information.
142       */
143      private static LDAPFilter createGreaterOrEqualFilter(
144                                  AttributeValueAssertion ava) {
145        return LDAPFilter.createGreaterOrEqualFilter(ava.getName(),
146                                              new ASN1OctetString(ava.getValue()));
147      }
148    
149      /**
150       * Returns a new LessOrEqual search filter with the provided information.
151       *
152       * @param ava the attribute value assertion for this LessOrEqual filter.
153       *
154       * @return a new LessOrEqual search filter with the provided information.
155       */
156      private static LDAPFilter createLessOrEqualFilter(
157                                  AttributeValueAssertion ava) {
158        return LDAPFilter.createLessOrEqualFilter(ava.getName(),
159                                              new ASN1OctetString(ava.getValue()));
160      }
161    
162      /**
163       * Returns a new NOT search filter with the provided information.
164       *
165       * @param filter the filter for this NOT filter.
166       *
167       * @return a new NOT search filter with the provided information.
168       *
169       * @throws LDAPException an LDAPException is thrown if the creation of the
170       *                       provided filter fails.
171       */
172      private static LDAPFilter createNOTFilter(Filter filter)
173              throws LDAPException {
174        return LDAPFilter.createNOTFilter(createFilter(filter));
175      }
176    
177      /**
178       * Returns a new OR search filter with the provided filter components.
179       *
180       * @param filterSet The filter components for this filter
181       *
182       * @return a new OR search filter with the provided filter components.
183       *
184       * @throws LDAPException an LDAPException is thrown if the creation of a
185       *                       filter component fails.
186       */
187      private static LDAPFilter createORFilter(FilterSet filterSet)
188              throws LDAPException {
189        List<JAXBElement<?>> list = filterSet.getFilterGroup();
190        ArrayList<RawFilter> filters = new ArrayList<RawFilter>(list.size());
191    
192        for(JAXBElement<?> filter : list) {
193          filters.add(createFilter(filter));
194        }
195        return LDAPFilter.createORFilter(filters);
196      }
197    
198      /**
199       * Returns a new Present search filter with the provided information.
200       *
201       * @param ad the attribute description for this Present filter.
202       *
203       * @returna new Present search filter with the provided information.
204       *
205       * @throws LDAPException an LDAPException is thrown if the ASN.1 element
206       *                       provided by the attribute description cannot be
207       *                       decoded as a raw search filter.
208       */
209      private static LDAPFilter createPresentFilter(AttributeDescription ad)
210              throws LDAPException {
211        return LDAPFilter.decode(
212                 new StringBuilder(ad.getName()).append("=*").toString());
213      }
214    
215      /**
216       * Returns a new Substring search filter with the provided information.
217       *
218       * @param sf the substring filter for this Substring filter.
219       *
220       * @return a new Substring search filter with the provided information.
221       */
222      private static LDAPFilter createSubstringFilter(SubstringFilter sf) {
223        List<String> anys = sf.getAny();
224        ArrayList<ByteString> subAnyElements =
225                                             new ArrayList<ByteString>(anys.size());
226    
227        for(String s : anys) {
228          subAnyElements.add(new ASN1OctetString(s));
229        }
230        return LDAPFilter.createSubstringFilter(sf.getName(),
231                                            new ASN1OctetString(sf.getInitial()),
232                                            subAnyElements,
233                                            new ASN1OctetString(sf.getFinal()));
234      }
235    
236      /**
237       * Returns a new LDAPFilter according to the tag name of the provided element
238       * that can be "and", "or", "not", "equalityMatch", "substrings",
239       * "greaterOrEqual", "lessOrEqual", "present", "approxMatch",
240       * "extensibleMatch".
241       *
242       * @param xmlElement a JAXBElement that contains the name of the filter to
243       *                   create and the associated argument.
244       *
245       * @return a new LDAPFilter according to the tag name of the provided element.
246       *
247       * @throws LDAPException an LDAPException is thrown if the creation of the
248       *                       targeted filter fails.
249       */
250      private static LDAPFilter createFilter(JAXBElement<?> xmlElement)
251              throws LDAPException {
252        LDAPFilter result = null;
253    
254        String filterName = xmlElement.getName().getLocalPart();
255    
256        if ( "and".equals(filterName) ) {
257          // <xsd:element name="and" type="FilterSet"/>
258          result = createANDFilter((FilterSet)xmlElement.getValue());
259        }
260        else if ( "or".equals(filterName) ) {
261          // <xsd:element name="or" type="FilterSet"/>
262          result = createORFilter((FilterSet)xmlElement.getValue());
263        }
264        else if ( "not".equals(filterName) ) {
265          // <xsd:element name="not" type="Filter"/>
266          result = createNOTFilter((Filter)xmlElement.getValue());
267        }
268        else if ( "equalityMatch".equals(filterName) ) {
269          // <xsd:element name="equalityMatch" type="AttributeValueAssertion"/>
270          result = createEqualityFilter((AttributeValueAssertion)
271                                                             xmlElement.getValue());
272        }
273        else if ( "substrings".equals(filterName) ) {
274          // <xsd:element name="substrings" type="SubstringFilter"/>
275          result = createSubstringFilter((SubstringFilter)xmlElement.getValue());
276        }
277        else if ( "greaterOrEqual".equals(filterName) ) {
278          // <xsd:element name="greaterOrEqual" type="AttributeValueAssertion"/>
279          result = createGreaterOrEqualFilter((AttributeValueAssertion)
280                                                             xmlElement.getValue());
281        }
282        else if ( "lessOrEqual".equals(filterName) ) {
283          // <xsd:element name="lessOrEqual" type="AttributeValueAssertion"/>
284          result = createLessOrEqualFilter((AttributeValueAssertion)
285                                                             xmlElement.getValue());
286        }
287        else if ( "present".equals(filterName) ) {
288          // <xsd:element name="present" type="AttributeDescription"/>
289          result = createPresentFilter((AttributeDescription)xmlElement.getValue());
290        }
291        else if ( "approxMatch".equals(filterName) ) {
292          // <xsd:element name="approxMatch" type="AttributeValueAssertion"/>
293          result = createApproximateFilter((AttributeValueAssertion)
294                                                             xmlElement.getValue());
295        }
296        else if ( "extensibleMatch".equals(filterName) ) {
297          // <xsd:element name="extensibleMatch" type="MatchingRuleAssertion"/>
298          result = createExtensibleFilter((MatchingRuleAssertion)
299                                                             xmlElement.getValue());
300        }
301        return result;
302      }
303    
304      /**
305       * Returns a new LDAPFilter according to the filter assigned to the provided
306       * filter.
307       *
308       * @param filter a filter that contains the object filter to create.
309       *
310       * @return a new LDAPFilter according to the filter assigned to the provided
311       *         filter.
312       *
313       * @throws LDAPException an LDAPException is thrown if the creation of the
314       *                       targeted filter fails.
315       */
316      private static LDAPFilter createFilter(Filter filter)
317              throws LDAPException {
318    
319        LDAPFilter result = null;
320    
321        if ( filter.getAnd() != null ) {
322          result = createANDFilter(filter.getAnd());
323        }
324        else if ( filter.getApproxMatch() != null ) {
325          result = createApproximateFilter(filter.getApproxMatch());
326        }
327        else if ( filter.getEqualityMatch() != null ) {
328          result = createEqualityFilter(filter.getEqualityMatch());
329        }
330        else if ( filter.getExtensibleMatch() != null ) {
331          result = createExtensibleFilter(filter.getExtensibleMatch());
332        }
333        else if ( filter.getGreaterOrEqual() != null ) {
334          result = createGreaterOrEqualFilter(filter.getGreaterOrEqual());
335        }
336        else if ( filter.getLessOrEqual() != null ) {
337          result = createLessOrEqualFilter(filter.getLessOrEqual());
338        }
339        else if ( filter.getNot() != null ) {
340          result = createNOTFilter(filter.getNot());
341        }
342        else if ( filter.getOr() != null ) {
343          result = createORFilter(filter.getOr());
344        }
345        else if ( filter.getPresent() != null ) {
346          result = createPresentFilter(filter.getPresent());
347        }
348        else if ( filter.getSubstrings() != null ) {
349          result = createSubstringFilter(filter.getSubstrings());
350        }
351        return result;
352      }
353    
354      /**
355       * Perform the LDAP SEARCH operation and send the result back to the
356       * client.
357       *
358       * @param  objFactory     The object factory for this operation.
359       * @param  searchRequest  The search request for this operation.
360       *
361       * @return  The result of the add operation.
362       *
363       * @throws  IOException  If an I/O problem occurs.
364       *
365       * @throws  LDAPException  If an error occurs while interacting with an LDAP
366       *                         element.
367       */
368      public SearchResponse doSearch(ObjectFactory objFactory,
369             SearchRequest searchRequest)
370             throws IOException, LDAPException
371      {
372        SearchResponse searchResponse = objFactory.createSearchResponse();
373        searchResponse.setRequestID(searchRequest.getRequestID());
374    
375        LDAPFilter filter = createFilter(searchRequest.getFilter());
376    
377        DereferencePolicy derefPolicy = DereferencePolicy.NEVER_DEREF_ALIASES;
378        String derefStr = searchRequest.getDerefAliases().toLowerCase();
379        if (derefStr.equals("derefinsearching"))
380        {
381          derefPolicy = DereferencePolicy.DEREF_IN_SEARCHING;
382        }
383        else if (derefStr.equals("dereffindingbaseobj"))
384        {
385          derefPolicy = DereferencePolicy.DEREF_FINDING_BASE_OBJECT;
386        }
387        else if (derefStr.equals("derefalways"))
388        {
389          derefPolicy = DereferencePolicy.DEREF_ALWAYS;
390        }
391    
392        SearchScope scope = SearchScope.WHOLE_SUBTREE;
393        String scopeStr = searchRequest.getScope().toLowerCase();
394        if(scopeStr.equals("singlelevel") || scopeStr.equals("one"))
395        {
396          scope = SearchScope.SINGLE_LEVEL;
397        } else if(scopeStr.equals("baseobject") || scopeStr.equals("base"))
398        {
399          scope = SearchScope.BASE_OBJECT;
400        }
401    
402        LinkedHashSet<String> attributes = new LinkedHashSet<String>();
403        // Get the list of attributes.
404        AttributeDescriptions attrDescriptions = searchRequest.getAttributes();
405        if(attrDescriptions != null)
406        {
407          List<AttributeDescription> attrDesc = attrDescriptions.getAttribute();
408          for(AttributeDescription desc : attrDesc)
409          {
410            attributes.add(desc.getName());
411          }
412        }
413    
414        SearchRequestProtocolOp protocolOp = new SearchRequestProtocolOp(
415            new ASN1OctetString(searchRequest.getDn()),
416            scope, derefPolicy,
417                    (int) searchRequest.getSizeLimit(),
418            (int) searchRequest.getTimeLimit(),
419            searchRequest.isTypesOnly(), filter, attributes);
420        try
421        {
422          LDAPMessage msg = new LDAPMessage(DSMLServlet.nextMessageID(),
423                                            protocolOp);
424          connection.getLDAPWriter().writeMessage(msg);
425    
426          byte opType;
427          do
428          {
429            int resultCode = 0;
430            Message errorMessage = null;
431            LDAPMessage responseMessage =
432                 connection.getLDAPReader().readMessage();
433    
434            opType = responseMessage.getProtocolOpType();
435            switch(opType)
436            {
437              case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
438                SearchResultEntryProtocolOp searchEntryOp =
439                  responseMessage.getSearchResultEntryProtocolOp();
440    
441                SearchResultEntry entry = objFactory.createSearchResultEntry();
442                java.util.List<DsmlAttr> attrList = entry.getAttr();
443    
444                LinkedList<LDAPAttribute> attrs = searchEntryOp.getAttributes();
445    
446                for(LDAPAttribute attr : attrs)
447                {
448                  String nm = attr.getAttributeType();
449                  DsmlAttr dsmlAttr = objFactory.createDsmlAttr();
450    
451                  dsmlAttr.setName(nm);
452                  List<String> dsmlAttrVal = dsmlAttr.getValue();
453                  ArrayList<ASN1OctetString> vals = attr.getValues();
454                  for(ASN1OctetString val : vals)
455                  {
456                    dsmlAttrVal.add(val.toString());
457                  }
458                  attrList.add(dsmlAttr);
459                }
460    
461                entry.setDn(searchEntryOp.getDN().toString());
462                searchResponse.getSearchResultEntry().add(entry);
463                break;
464    
465              case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
466                SearchResultReferenceProtocolOp searchRefOp =
467                  responseMessage.getSearchResultReferenceProtocolOp();
468                break;
469    
470              case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
471                SearchResultDoneProtocolOp searchOp =
472                  responseMessage.getSearchResultDoneProtocolOp();
473                resultCode = searchOp.getResultCode();
474                errorMessage = searchOp.getErrorMessage();
475                LDAPResult result = objFactory.createLDAPResult();
476                ResultCode code = objFactory.createResultCode();
477                code.setCode(resultCode);
478                result.setResultCode(code);
479                result.setErrorMessage(
480                        errorMessage != null ? errorMessage.toString() : null);
481                if(searchOp.getMatchedDN() != null)
482                {
483                   result.setMatchedDN(searchOp.getMatchedDN().toString());
484                }
485                searchResponse.setSearchResultDone(result);
486                break;
487              default:
488                 throw new RuntimeException("Invalid protocol operation:" + opType);
489             }
490          } while(opType != LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE);
491    
492        } catch(ASN1Exception ae)
493        {
494          ae.printStackTrace();
495          throw new IOException(ae.getMessage());
496        }
497    
498        return searchResponse;
499      }
500    }
501