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.server.protocols.ldap;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    import java.util.LinkedHashSet;
035    
036    import org.opends.server.protocols.asn1.ASN1Boolean;
037    import org.opends.server.protocols.asn1.ASN1Element;
038    import org.opends.server.protocols.asn1.ASN1Enumerated;
039    import org.opends.server.protocols.asn1.ASN1Integer;
040    import org.opends.server.protocols.asn1.ASN1OctetString;
041    import org.opends.server.protocols.asn1.ASN1Sequence;
042    import org.opends.server.types.DebugLogLevel;
043    import org.opends.server.types.DereferencePolicy;
044    import org.opends.server.types.LDAPException;
045    import org.opends.server.types.SearchScope;
046    
047    import static org.opends.server.loggers.debug.DebugLogger.*;
048    import org.opends.server.loggers.debug.DebugTracer;
049    import static org.opends.messages.ProtocolMessages.*;
050    import static org.opends.server.protocols.ldap.LDAPConstants.*;
051    import static org.opends.server.protocols.ldap.LDAPResultCode.*;
052    import static org.opends.server.util.ServerConstants.*;
053    
054    
055    
056    /**
057     * This class defines the structures and methods for an LDAP search request
058     * protocol op, which is used to locate entries based on a set of criteria.
059     */
060    public class SearchRequestProtocolOp
061           extends ProtocolOp
062    {
063      /**
064       * The tracer object for the debug logger.
065       */
066      private static final DebugTracer TRACER = getTracer();
067    
068      // The typesOnly flag for this search request.
069      private boolean typesOnly;
070    
071      // The alias dereferencing policy for this search request.
072      private DereferencePolicy dereferencePolicy;
073    
074      // The base DN for this search request.
075      private ASN1OctetString baseDN;
076    
077      // The size limit for this search request.
078      private int sizeLimit;
079    
080      // The time limit for this search request.
081      private int timeLimit;
082    
083      // The filter for this search request.
084      private LDAPFilter filter;
085    
086      // The set of requested attributes for this search request.
087      private LinkedHashSet<String> attributes;
088    
089      // The scope for this search request.
090      private SearchScope scope;
091    
092    
093    
094      /**
095       * Creates a new search request protocol op with the provided information.
096       *
097       * @param  baseDN             The base DN for this search request.
098       * @param  scope              The scope for this search request.
099       * @param  dereferencePolicy  The alias dereferencing policy for this search
100       *                            request.
101       * @param  sizeLimit          The size limit for this search request.
102       * @param  timeLimit          The time limit for this search request.
103       * @param  typesOnly          The typesOnly flag for this search request.
104       * @param  filter             The filter for this search request.
105       * @param  attributes         The set of requested attributes for this search
106       *                            request.
107       */
108      public SearchRequestProtocolOp(ASN1OctetString baseDN, SearchScope scope,
109                                     DereferencePolicy dereferencePolicy,
110                                     int sizeLimit, int timeLimit,
111                                     boolean typesOnly, LDAPFilter filter,
112                                     LinkedHashSet<String> attributes)
113      {
114        this.baseDN            = baseDN;
115        this.scope             = scope;
116        this.dereferencePolicy = dereferencePolicy;
117        this.sizeLimit         = sizeLimit;
118        this.timeLimit         = timeLimit;
119        this.typesOnly         = typesOnly;
120        this.filter            = filter;
121    
122        if (attributes == null)
123        {
124          this.attributes = new LinkedHashSet<String>(0);
125        }
126        else
127        {
128          this.attributes = attributes;
129        }
130      }
131    
132    
133    
134      /**
135       * Retrieves the base DN for this search request.
136       *
137       * @return  The base DN for this search request.
138       */
139      public ASN1OctetString getBaseDN()
140      {
141        return baseDN;
142      }
143    
144    
145    
146      /**
147       * Specifies the base DN for this search request.
148       *
149       * @param  baseDN  The base DN for this search request.
150       */
151      public void setBaseDN(ASN1OctetString baseDN)
152      {
153        this.baseDN = baseDN;
154      }
155    
156    
157    
158      /**
159       * Retrieves the scope for this search request.
160       *
161       * @return  The scope for this search request.
162       */
163      public SearchScope getScope()
164      {
165        return scope;
166      }
167    
168    
169    
170      /**
171       * Specifies the scope for this search request.
172       *
173       * @param  scope  The scope for this search request.
174       */
175      public void setScope(SearchScope scope)
176      {
177        this.scope = scope;
178      }
179    
180    
181    
182      /**
183       * Retrieves the alias dereferencing policy for this search request.
184       *
185       * @return  The alias dereferencing policy for this search request.
186       */
187      public DereferencePolicy getDereferencePolicy()
188      {
189        return dereferencePolicy;
190      }
191    
192    
193    
194      /**
195       * Specifies the alias dereferencing policy for this search request.
196       *
197       * @param  dereferencePolicy  The alias dereferencing policy for this search
198       *                            request.
199       */
200      public void setDereferencePolicy(DereferencePolicy dereferencePolicy)
201      {
202        this.dereferencePolicy = dereferencePolicy;
203      }
204    
205    
206    
207      /**
208       * Retrieves the size limit for this search request.
209       *
210       * @return  The size limit for this search request.
211       */
212      public int getSizeLimit()
213      {
214        return sizeLimit;
215      }
216    
217    
218    
219      /**
220       * Specifies the size limit for this search request.
221       *
222       * @param  sizeLimit  The size limit for this search request.
223       */
224      public void setSizeLimit(int sizeLimit)
225      {
226        this.sizeLimit = sizeLimit;
227      }
228    
229    
230    
231      /**
232       * Retrieves the time limit for this search request.
233       *
234       * @return  The time limit for this search request.
235       */
236      public int getTimeLimit()
237      {
238        return timeLimit;
239      }
240    
241    
242    
243      /**
244       * Specifies the time limit for this search request.
245       *
246       * @param  timeLimit  The time limit for this search request.
247       */
248      public void setTimeLimit(int timeLimit)
249      {
250        this.timeLimit = timeLimit;
251      }
252    
253    
254    
255      /**
256       * Retrieves the value of the typesOnly flag for this search request.
257       *
258       * @return  The value of tye typesOnly flag for this search request.
259       */
260      public boolean getTypesOnly()
261      {
262        return typesOnly;
263      }
264    
265    
266    
267      /**
268       * Specifies the value of the typesOnly flag for this search request.
269       *
270       * @param  typesOnly  The value of the typesOnly flag for this search request.
271       */
272      public void setTypesOnly(boolean typesOnly)
273      {
274        this.typesOnly = typesOnly;
275      }
276    
277    
278    
279      /**
280       * Retrieves the filter for this search request.
281       *
282       * @return  The filter for this search request.
283       */
284      public LDAPFilter getFilter()
285      {
286        return filter;
287      }
288    
289    
290    
291      /**
292       * Specifies the filter for this search request.
293       *
294       * @param  filter  The filter for this search request.
295       */
296      public void setFilter(LDAPFilter filter)
297      {
298        this.filter = filter;
299      }
300    
301    
302    
303      /**
304       * Retrieves the set of requested attributes for this search request.  The
305       * returned list may be modified by the caller.
306       *
307       * @return  The set of requested attributes for this search request.
308       */
309      public LinkedHashSet<String> getAttributes()
310      {
311        return attributes;
312      }
313    
314    
315    
316      /**
317       * Specifies the set of requested attributes for this search request.
318       *
319       * @param  attributes  The set of requested attributes for this search
320       *                     request.
321       */
322      public void setAttributes(LinkedHashSet<String> attributes)
323      {
324        if (attributes == null)
325        {
326          this.attributes.clear();
327        }
328        else
329        {
330          this.attributes = attributes;
331        }
332      }
333    
334    
335    
336      /**
337       * Retrieves the BER type for this protocol op.
338       *
339       * @return  The BER type for this protocol op.
340       */
341      public byte getType()
342      {
343        return OP_TYPE_SEARCH_REQUEST;
344      }
345    
346    
347    
348      /**
349       * Retrieves the name for this protocol op type.
350       *
351       * @return  The name for this protocol op type.
352       */
353      public String getProtocolOpName()
354      {
355        return "Search Request";
356      }
357    
358    
359    
360      /**
361       * Encodes this protocol op to an ASN.1 element suitable for including in an
362       * LDAP message.
363       *
364       * @return  The ASN.1 element containing the encoded protocol op.
365       */
366      public ASN1Element encode()
367      {
368        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(8);
369        elements.add(baseDN);
370        elements.add(new ASN1Enumerated(scope.intValue()));
371        elements.add(new ASN1Enumerated(dereferencePolicy.intValue()));
372        elements.add(new ASN1Integer(sizeLimit));
373        elements.add(new ASN1Integer(timeLimit));
374        elements.add(new ASN1Boolean(typesOnly));
375        elements.add(filter.encode());
376    
377        ArrayList<ASN1Element> attrElements =
378             new ArrayList<ASN1Element>(attributes.size());
379        for (String attribute : attributes)
380        {
381          attrElements.add(new ASN1OctetString(attribute));
382        }
383        elements.add(new ASN1Sequence(attrElements));
384    
385        return new ASN1Sequence(OP_TYPE_SEARCH_REQUEST, elements);
386      }
387    
388    
389    
390      /**
391       * Decodes the provided ASN.1 element as an LDAP search request protocol op.
392       *
393       * @param  element  The ASN.1 element to decode.
394       *
395       * @return  The decoded LDAP search request protocol op.
396       *
397       * @throws  LDAPException  If a problem occurs while decoding the provided
398       *                         ASN.1 element as an LDAP search request protocol
399       *                         op.
400       */
401      public static SearchRequestProtocolOp decodeSearchRequest(ASN1Element element)
402             throws LDAPException
403      {
404        ArrayList<ASN1Element> elements;
405        try
406        {
407          elements = element.decodeAsSequence().elements();
408        }
409        catch (Exception e)
410        {
411          if (debugEnabled())
412          {
413            TRACER.debugCaught(DebugLogLevel.ERROR, e);
414          }
415    
416          Message message =
417              ERR_LDAP_SEARCH_REQUEST_DECODE_SEQUENCE.get(String.valueOf(e));
418          throw new LDAPException(PROTOCOL_ERROR, message, e);
419        }
420    
421    
422        int numElements = elements.size();
423        if (numElements != 8)
424        {
425          Message message =
426              ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_ELEMENT_COUNT.get(numElements);
427          throw new LDAPException(PROTOCOL_ERROR, message);
428        }
429    
430    
431        ASN1OctetString baseDN;
432        try
433        {
434          baseDN = elements.get(0).decodeAsOctetString();
435        }
436        catch (Exception e)
437        {
438          if (debugEnabled())
439          {
440            TRACER.debugCaught(DebugLogLevel.ERROR, e);
441          }
442    
443          Message message =
444              ERR_LDAP_SEARCH_REQUEST_DECODE_BASE.get(String.valueOf(e));
445          throw new LDAPException(PROTOCOL_ERROR, message, e);
446        }
447    
448    
449        SearchScope scope;
450        try
451        {
452          switch (elements.get(1).decodeAsEnumerated().intValue())
453          {
454            case SCOPE_BASE_OBJECT:
455              scope = SearchScope.BASE_OBJECT;
456              break;
457            case SCOPE_SINGLE_LEVEL:
458              scope = SearchScope.SINGLE_LEVEL;
459              break;
460            case SCOPE_WHOLE_SUBTREE:
461              scope = SearchScope.WHOLE_SUBTREE;
462              break;
463            case SCOPE_SUBORDINATE_SUBTREE:
464              scope = SearchScope.SUBORDINATE_SUBTREE;
465              break;
466            default:
467              int    scopeValue = elements.get(1).decodeAsEnumerated().intValue();
468              Message message =
469                  ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_SCOPE.get(scopeValue);
470              throw new LDAPException(PROTOCOL_ERROR, message);
471          }
472        }
473        catch (LDAPException le)
474        {
475          throw le;
476        }
477        catch (Exception e)
478        {
479          if (debugEnabled())
480          {
481            TRACER.debugCaught(DebugLogLevel.ERROR, e);
482          }
483    
484          Message message =
485              ERR_LDAP_SEARCH_REQUEST_DECODE_SCOPE.get(String.valueOf(e));
486          throw new LDAPException(PROTOCOL_ERROR, message, e);
487        }
488    
489    
490        DereferencePolicy dereferencePolicy;
491        try
492        {
493          switch (elements.get(2).decodeAsEnumerated().intValue())
494          {
495            case DEREF_NEVER:
496              dereferencePolicy = DereferencePolicy.NEVER_DEREF_ALIASES;
497              break;
498            case DEREF_IN_SEARCHING:
499              dereferencePolicy = DereferencePolicy.DEREF_IN_SEARCHING;
500              break;
501            case DEREF_FINDING_BASE:
502              dereferencePolicy = DereferencePolicy.DEREF_FINDING_BASE_OBJECT;
503              break;
504            case DEREF_ALWAYS:
505              dereferencePolicy = DereferencePolicy.DEREF_ALWAYS;
506              break;
507            default:
508              int    derefValue = elements.get(2).decodeAsEnumerated().intValue();
509              Message message =
510                  ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_DEREF.get(derefValue);
511              throw new LDAPException(PROTOCOL_ERROR, message);
512          }
513        }
514        catch (LDAPException le)
515        {
516          throw le;
517        }
518        catch (Exception e)
519        {
520          if (debugEnabled())
521          {
522            TRACER.debugCaught(DebugLogLevel.ERROR, e);
523          }
524    
525          Message message =
526              ERR_LDAP_SEARCH_REQUEST_DECODE_DEREF.get(String.valueOf(e));
527          throw new LDAPException(PROTOCOL_ERROR, message, e);
528        }
529    
530    
531        int sizeLimit;
532        try
533        {
534          sizeLimit = elements.get(3).decodeAsInteger().intValue();
535        }
536        catch (Exception e)
537        {
538          if (debugEnabled())
539          {
540            TRACER.debugCaught(DebugLogLevel.ERROR, e);
541          }
542    
543          Message message =
544              ERR_LDAP_SEARCH_REQUEST_DECODE_SIZE_LIMIT.get(String.valueOf(e));
545          throw new LDAPException(PROTOCOL_ERROR, message, e);
546        }
547    
548    
549        int timeLimit;
550        try
551        {
552          timeLimit = elements.get(4).decodeAsInteger().intValue();
553        }
554        catch (Exception e)
555        {
556          if (debugEnabled())
557          {
558            TRACER.debugCaught(DebugLogLevel.ERROR, e);
559          }
560    
561          Message message =
562              ERR_LDAP_SEARCH_REQUEST_DECODE_TIME_LIMIT.get(String.valueOf(e));
563          throw new LDAPException(PROTOCOL_ERROR, message, e);
564        }
565    
566    
567        boolean typesOnly;
568        try
569        {
570          typesOnly = elements.get(5).decodeAsBoolean().booleanValue();
571        }
572        catch (Exception e)
573        {
574          if (debugEnabled())
575          {
576            TRACER.debugCaught(DebugLogLevel.ERROR, e);
577          }
578    
579          Message message =
580              ERR_LDAP_SEARCH_REQUEST_DECODE_TYPES_ONLY.get(String.valueOf(e));
581          throw new LDAPException(PROTOCOL_ERROR, message, e);
582        }
583    
584    
585        LDAPFilter filter;
586        try
587        {
588          filter = LDAPFilter.decode(elements.get(6));
589        }
590        catch (Exception e)
591        {
592          if (debugEnabled())
593          {
594            TRACER.debugCaught(DebugLogLevel.ERROR, e);
595          }
596    
597          Message message =
598              ERR_LDAP_SEARCH_REQUEST_DECODE_FILTER.get(String.valueOf(e));
599          throw new LDAPException(PROTOCOL_ERROR, message, e);
600        }
601    
602    
603        LinkedHashSet<String> attributes;
604        try
605        {
606          ArrayList<ASN1Element> attrElements =
607               elements.get(7).decodeAsSequence().elements();
608          attributes = new LinkedHashSet<String>(attrElements.size());
609          for (ASN1Element e: attrElements)
610          {
611            attributes.add(e.decodeAsOctetString().stringValue());
612          }
613        }
614        catch (Exception e)
615        {
616          if (debugEnabled())
617          {
618            TRACER.debugCaught(DebugLogLevel.ERROR, e);
619          }
620    
621          Message message =
622              ERR_LDAP_SEARCH_REQUEST_DECODE_ATTRIBUTES.get(String.valueOf(e));
623          throw new LDAPException(PROTOCOL_ERROR, message, e);
624        }
625    
626    
627        return new SearchRequestProtocolOp(baseDN, scope, dereferencePolicy,
628                                           sizeLimit, timeLimit, typesOnly, filter,
629                                           attributes);
630      }
631    
632    
633    
634      /**
635       * Appends a string representation of this LDAP protocol op to the provided
636       * buffer.
637       *
638       * @param  buffer  The buffer to which the string should be appended.
639       */
640      public void toString(StringBuilder buffer)
641      {
642        buffer.append("SearchRequest(baseDN=");
643        baseDN.toString(buffer);
644        buffer.append(", scope=");
645        buffer.append(String.valueOf(scope));
646        buffer.append(", derefPolicy=");
647        buffer.append(String.valueOf(dereferencePolicy));
648        buffer.append(", sizeLimit=");
649        buffer.append(sizeLimit);
650        buffer.append(", timeLimit=");
651        buffer.append(timeLimit);
652        buffer.append(", typesOnly=");
653        buffer.append(typesOnly);
654        buffer.append(", filter=");
655        filter.toString(buffer);
656        buffer.append(", attributes={");
657    
658        if ((attributes != null) && (! attributes.isEmpty()))
659        {
660          Iterator<String> iterator = attributes.iterator();
661          buffer.append(iterator.next());
662    
663          while (iterator.hasNext())
664          {
665            buffer.append(", ");
666            buffer.append(iterator.next());
667          }
668        }
669    
670        buffer.append("})");
671      }
672    
673    
674    
675      /**
676       * Appends a multi-line string representation of this LDAP protocol op to the
677       * provided buffer.
678       *
679       * @param  buffer  The buffer to which the information should be appended.
680       * @param  indent  The number of spaces from the margin that the lines should
681       *                 be indented.
682       */
683      public void toString(StringBuilder buffer, int indent)
684      {
685        StringBuilder indentBuf = new StringBuilder(indent);
686        for (int i=0 ; i < indent; i++)
687        {
688          indentBuf.append(' ');
689        }
690    
691        buffer.append(indentBuf);
692        buffer.append("Search Request");
693        buffer.append(EOL);
694    
695        buffer.append(indentBuf);
696        buffer.append("  Base DN:  ");
697        baseDN.toString(buffer);
698        buffer.append(EOL);
699    
700        buffer.append(indentBuf);
701        buffer.append("  Scope:  ");
702        buffer.append(String.valueOf(scope));
703        buffer.append(EOL);
704    
705        buffer.append(indentBuf);
706        buffer.append("  Dereference Policy:  ");
707        buffer.append(String.valueOf(dereferencePolicy));
708        buffer.append(EOL);
709    
710        buffer.append(indentBuf);
711        buffer.append("  Size Limit:  ");
712        buffer.append(sizeLimit);
713        buffer.append(EOL);
714    
715        buffer.append(indentBuf);
716        buffer.append("  Time Limit:  ");
717        buffer.append(timeLimit);
718        buffer.append(EOL);
719    
720        buffer.append(indentBuf);
721        buffer.append("  Types Only:  ");
722        buffer.append(typesOnly);
723        buffer.append(EOL);
724    
725        buffer.append(indentBuf);
726        buffer.append("  Filter:  ");
727        filter.toString(buffer);
728        buffer.append(EOL);
729    
730        buffer.append(indentBuf);
731        buffer.append("  Attributes:");
732        buffer.append(EOL);
733    
734        if (attributes != null)
735        {
736          for (String attribute : attributes)
737          {
738            buffer.append(indentBuf);
739            buffer.append("    ");
740            buffer.append(attribute);
741            buffer.append(EOL);
742          }
743        }
744      }
745    }
746