001/*
002 * Copyright 2009-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.protocol;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.List;
029
030import com.unboundid.asn1.ASN1Boolean;
031import com.unboundid.asn1.ASN1Buffer;
032import com.unboundid.asn1.ASN1BufferSequence;
033import com.unboundid.asn1.ASN1Element;
034import com.unboundid.asn1.ASN1Enumerated;
035import com.unboundid.asn1.ASN1Integer;
036import com.unboundid.asn1.ASN1OctetString;
037import com.unboundid.asn1.ASN1Sequence;
038import com.unboundid.asn1.ASN1StreamReader;
039import com.unboundid.asn1.ASN1StreamReaderSequence;
040import com.unboundid.ldap.sdk.Control;
041import com.unboundid.ldap.sdk.DereferencePolicy;
042import com.unboundid.ldap.sdk.Filter;
043import com.unboundid.ldap.sdk.LDAPException;
044import com.unboundid.ldap.sdk.ResultCode;
045import com.unboundid.ldap.sdk.SearchRequest;
046import com.unboundid.ldap.sdk.SearchScope;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.InternalUseOnly;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.protocol.ProtocolMessages.*;
053import static com.unboundid.util.Debug.*;
054import static com.unboundid.util.StaticUtils.*;
055
056
057
058/**
059 * This class provides an implementation of an LDAP search request protocol op.
060 */
061@InternalUseOnly()
062@NotMutable()
063@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064public final class SearchRequestProtocolOp
065       implements ProtocolOp
066{
067  /**
068   * The serial version UID for this serializable class.
069   */
070  private static final long serialVersionUID = -8521750809606744181L;
071
072
073
074  // The typesOnly flag for this search request.
075  private final boolean typesOnly;
076
077  // The dereference policy for this search request.
078  private final DereferencePolicy derefPolicy;
079
080  // The filter for this search request.
081  private final Filter filter;
082
083  // The size limit for this search request.
084  private final int sizeLimit;
085
086  // The time limit for this search request.
087  private final int timeLimit;
088
089  // The set of attributes for this search request.
090  private final List<String> attributes;
091
092  // The scope for this search request.
093  private final SearchScope scope;
094
095  // The base DN for this search request.
096  private final String baseDN;
097
098
099
100  /**
101   * Creates a new search request protocol op with the provided information.
102   *
103   * @param  baseDN       The base DN for this search request.
104   * @param  scope        The scope for this search request.
105   * @param  derefPolicy  The policy to use for aliases encountered during the
106   *                      search.
107   * @param  sizeLimit    The maximum number of entries to return for the
108   *                      search, or zero for no limit.
109   * @param  timeLimit    The maximum length of time to spend processing the
110   *                      search, or zero for no limit.
111   * @param  typesOnly    Indicates whether to return only attribute types or
112   *                      both types and values.
113   * @param  filter       The filter for this search request.
114   * @param  attributes   The names of attributes to include in matching
115   *                      entries.
116   */
117  public SearchRequestProtocolOp(final String baseDN, final SearchScope scope,
118              final DereferencePolicy derefPolicy, final int sizeLimit,
119              final int timeLimit, final boolean typesOnly, final Filter filter,
120              final List<String> attributes)
121  {
122    this.scope       = scope;
123    this.derefPolicy = derefPolicy;
124    this.typesOnly   = typesOnly;
125    this.filter      = filter;
126
127    if (baseDN == null)
128    {
129      this.baseDN = "";
130    }
131    else
132    {
133      this.baseDN = baseDN;
134    }
135
136    if (sizeLimit > 0)
137    {
138      this.sizeLimit = sizeLimit;
139    }
140    else
141    {
142      this.sizeLimit = 0;
143    }
144
145    if (timeLimit > 0)
146    {
147      this.timeLimit = timeLimit;
148    }
149    else
150    {
151      this.timeLimit = 0;
152    }
153
154    if (attributes == null)
155    {
156      this.attributes = Collections.emptyList();
157    }
158    else
159    {
160      this.attributes = Collections.unmodifiableList(attributes);
161    }
162  }
163
164
165
166  /**
167   * Creates a new search request protocol op from the provided search request
168   * object.
169   *
170   * @param  request  The search request object to use to create this protocol
171   *                  op.
172   */
173  public SearchRequestProtocolOp(final SearchRequest request)
174  {
175    baseDN      = request.getBaseDN();
176    scope       = request.getScope();
177    derefPolicy = request.getDereferencePolicy();
178    sizeLimit   = request.getSizeLimit();
179    timeLimit   = request.getTimeLimitSeconds();
180    typesOnly   = request.typesOnly();
181    filter      = request.getFilter();
182    attributes  = request.getAttributeList();
183  }
184
185
186
187  /**
188   * Creates a new search request protocol op read from the provided ASN.1
189   * stream reader.
190   *
191   * @param  reader  The ASN.1 stream reader from which to read the search
192   *                 request protocol op.
193   *
194   * @throws  LDAPException  If a problem occurs while reading or parsing the
195   *                         search request.
196   */
197  SearchRequestProtocolOp(final ASN1StreamReader reader)
198       throws LDAPException
199  {
200    try
201    {
202      reader.beginSequence();
203      baseDN      = reader.readString();
204      scope       = SearchScope.valueOf(reader.readEnumerated());
205      derefPolicy = DereferencePolicy.valueOf(reader.readEnumerated());
206      sizeLimit   = reader.readInteger();
207      timeLimit   = reader.readInteger();
208      typesOnly   = reader.readBoolean();
209      filter      = Filter.readFrom(reader);
210
211      final ArrayList<String> attrs = new ArrayList<String>(5);
212      final ASN1StreamReaderSequence attrSequence = reader.beginSequence();
213      while (attrSequence.hasMoreElements())
214      {
215        attrs.add(reader.readString());
216      }
217
218      attributes = Collections.unmodifiableList(attrs);
219    }
220    catch (final LDAPException le)
221    {
222      debugException(le);
223      throw le;
224    }
225    catch (final Exception e)
226    {
227      debugException(e);
228
229      throw new LDAPException(ResultCode.DECODING_ERROR,
230           ERR_SEARCH_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)), e);
231    }
232  }
233
234
235
236  /**
237   * Retrieves the base DN for this search request.
238   *
239   * @return  The base DN for this search request.
240   */
241  public String getBaseDN()
242  {
243    return baseDN;
244  }
245
246
247
248  /**
249   * Retrieves the scope for this search request.
250   *
251   * @return  The scope for this search request.
252   */
253  public SearchScope getScope()
254  {
255    return scope;
256  }
257
258
259
260  /**
261   * Retrieves the policy to use for any aliases encountered during the search.
262   *
263   * @return  The policy to use for any aliases encountered during the search.
264   */
265  public DereferencePolicy getDerefPolicy()
266  {
267    return derefPolicy;
268  }
269
270
271
272  /**
273   * Retrieves the maximum number of entries that the server should return for
274   * the search.
275   *
276   * @return  The maximum number of entries that the server should return for
277   *          the search, or zero if there is no limit.
278   */
279  public int getSizeLimit()
280  {
281    return sizeLimit;
282  }
283
284
285
286  /**
287   * Retrieves the maximum length of time in seconds the server should spend
288   * processing the search.
289   *
290   * @return  The maximum length of time in seconds the server should spend
291   *          processing the search, or zero if there is no limit.
292   */
293  public int getTimeLimit()
294  {
295    return timeLimit;
296  }
297
298
299
300  /**
301   * Indicates whether the server should return only attribute types or both
302   * attribute types and values.
303   *
304   * @return  {@code true} if the server should return only attribute types, or
305   *          {@code false} if both types and values should be returned.
306   */
307  public boolean typesOnly()
308  {
309    return typesOnly;
310  }
311
312
313
314  /**
315   * Retrieves the filter for this search request.
316   *
317   * @return  The filter for this search request.
318   */
319  public Filter getFilter()
320  {
321    return filter;
322  }
323
324
325
326  /**
327   * Retrieves the set of requested attributes for this search request.
328   *
329   * @return  The set of requested attributes for this search request.
330   */
331  public List<String> getAttributes()
332  {
333    return attributes;
334  }
335
336
337
338  /**
339   * {@inheritDoc}
340   */
341  @Override()
342  public byte getProtocolOpType()
343  {
344    return LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST;
345  }
346
347
348
349  /**
350   * {@inheritDoc}
351   */
352  @Override()
353  public ASN1Element encodeProtocolOp()
354  {
355    final ArrayList<ASN1Element> attrElements =
356         new ArrayList<ASN1Element>(attributes.size());
357    for (final String attribute : attributes)
358    {
359      attrElements.add(new ASN1OctetString(attribute));
360    }
361
362    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST,
363         new ASN1OctetString(baseDN),
364         new ASN1Enumerated(scope.intValue()),
365         new ASN1Enumerated(derefPolicy.intValue()),
366         new ASN1Integer(sizeLimit),
367         new ASN1Integer(timeLimit),
368         new ASN1Boolean(typesOnly),
369         filter.encode(),
370         new ASN1Sequence(attrElements));
371  }
372
373
374
375  /**
376   * Decodes the provided ASN.1 element as a search request protocol op.
377   *
378   * @param  element  The ASN.1 element to be decoded.
379   *
380   * @return  The decoded search request protocol op.
381   *
382   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
383   *                         a search request protocol op.
384   */
385  public static SearchRequestProtocolOp decodeProtocolOp(
386                                             final ASN1Element element)
387         throws LDAPException
388  {
389    try
390    {
391      final ASN1Element[] elements =
392           ASN1Sequence.decodeAsSequence(element).elements();
393      final String baseDN =
394           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
395      final SearchScope scope = SearchScope.valueOf(
396           ASN1Enumerated.decodeAsEnumerated(elements[1]).intValue());
397      final DereferencePolicy derefPolicy = DereferencePolicy.valueOf(
398           ASN1Enumerated.decodeAsEnumerated(elements[2]).intValue());
399      final int sizeLimit = ASN1Integer.decodeAsInteger(elements[3]).intValue();
400      final int timeLimit = ASN1Integer.decodeAsInteger(elements[4]).intValue();
401      final boolean typesOnly =
402           ASN1Boolean.decodeAsBoolean(elements[5]).booleanValue();
403      final Filter filter = Filter.decode(elements[6]);
404
405      final ASN1Element[] attrElements =
406           ASN1Sequence.decodeAsSequence(elements[7]).elements();
407      final ArrayList<String> attributes =
408           new ArrayList<String>(attrElements.length);
409      for (final ASN1Element e : attrElements)
410      {
411        attributes.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
412      }
413
414      return new SearchRequestProtocolOp(baseDN, scope, derefPolicy, sizeLimit,
415           timeLimit, typesOnly, filter, attributes);
416    }
417    catch (final Exception e)
418    {
419      debugException(e);
420      throw new LDAPException(ResultCode.DECODING_ERROR,
421           ERR_SEARCH_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)),
422           e);
423    }
424  }
425
426
427
428  /**
429   * {@inheritDoc}
430   */
431  @Override()
432  public void writeTo(final ASN1Buffer buffer)
433  {
434    final ASN1BufferSequence opSequence =
435         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST);
436    buffer.addOctetString(baseDN);
437    buffer.addEnumerated(scope.intValue());
438    buffer.addEnumerated(derefPolicy.intValue());
439    buffer.addInteger(sizeLimit);
440    buffer.addInteger(timeLimit);
441    buffer.addBoolean(typesOnly);
442    filter.writeTo(buffer);
443
444    final ASN1BufferSequence attrSequence = buffer.beginSequence();
445    for (final String s : attributes)
446    {
447      buffer.addOctetString(s);
448    }
449    attrSequence.end();
450    opSequence.end();
451  }
452
453
454
455  /**
456   * Creates a search request from this protocol op.
457   *
458   * @param  controls  The set of controls to include in the search request.
459   *                   It may be empty or {@code null} if no controls should be
460   *                   included.
461   *
462   * @return  The search request that was created.
463   */
464  public SearchRequest toSearchRequest(final Control... controls)
465  {
466    final String[] attrArray = new String[attributes.size()];
467    attributes.toArray(attrArray);
468
469    return new SearchRequest(null, controls, baseDN, scope, derefPolicy,
470         sizeLimit, timeLimit, typesOnly, filter, attrArray);
471  }
472
473
474
475  /**
476   * Retrieves a string representation of this protocol op.
477   *
478   * @return  A string representation of this protocol op.
479   */
480  @Override()
481  public String toString()
482  {
483    final StringBuilder buffer = new StringBuilder();
484    toString(buffer);
485    return buffer.toString();
486  }
487
488
489
490  /**
491   * {@inheritDoc}
492   */
493  @Override()
494  public void toString(final StringBuilder buffer)
495  {
496    buffer.append("SearchRequestProtocolOp(baseDN='");
497    buffer.append(baseDN);
498    buffer.append("', scope='");
499    buffer.append(scope.toString());
500    buffer.append("', derefPolicy='");
501    buffer.append(derefPolicy.toString());
502    buffer.append("', sizeLimit=");
503    buffer.append(sizeLimit);
504    buffer.append(", timeLimit=");
505    buffer.append(timeLimit);
506    buffer.append(", typesOnly=");
507    buffer.append(typesOnly);
508    buffer.append(", filter='");
509    filter.toString(buffer);
510    buffer.append("', attributes={");
511
512    final Iterator<String> iterator = attributes.iterator();
513    while (iterator.hasNext())
514    {
515      buffer.append(iterator.next());
516      if (iterator.hasNext())
517      {
518        buffer.append(',');
519      }
520    }
521
522    buffer.append("})");
523  }
524}