001/*
002 * Copyright 2009-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2019 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.Debug;
048import com.unboundid.util.InternalUseOnly;
049import com.unboundid.util.NotMutable;
050import com.unboundid.util.StaticUtils;
051import com.unboundid.util.ThreadSafety;
052import com.unboundid.util.ThreadSafetyLevel;
053
054import static com.unboundid.ldap.protocol.ProtocolMessages.*;
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<>(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      Debug.debugException(le);
223      throw le;
224    }
225    catch (final Exception e)
226    {
227      Debug.debugException(e);
228
229      throw new LDAPException(ResultCode.DECODING_ERROR,
230           ERR_SEARCH_REQUEST_CANNOT_DECODE.get(
231                StaticUtils.getExceptionMessage(e)),
232           e);
233    }
234  }
235
236
237
238  /**
239   * Retrieves the base DN for this search request.
240   *
241   * @return  The base DN for this search request.
242   */
243  public String getBaseDN()
244  {
245    return baseDN;
246  }
247
248
249
250  /**
251   * Retrieves the scope for this search request.
252   *
253   * @return  The scope for this search request.
254   */
255  public SearchScope getScope()
256  {
257    return scope;
258  }
259
260
261
262  /**
263   * Retrieves the policy to use for any aliases encountered during the search.
264   *
265   * @return  The policy to use for any aliases encountered during the search.
266   */
267  public DereferencePolicy getDerefPolicy()
268  {
269    return derefPolicy;
270  }
271
272
273
274  /**
275   * Retrieves the maximum number of entries that the server should return for
276   * the search.
277   *
278   * @return  The maximum number of entries that the server should return for
279   *          the search, or zero if there is no limit.
280   */
281  public int getSizeLimit()
282  {
283    return sizeLimit;
284  }
285
286
287
288  /**
289   * Retrieves the maximum length of time in seconds the server should spend
290   * processing the search.
291   *
292   * @return  The maximum length of time in seconds the server should spend
293   *          processing the search, or zero if there is no limit.
294   */
295  public int getTimeLimit()
296  {
297    return timeLimit;
298  }
299
300
301
302  /**
303   * Indicates whether the server should return only attribute types or both
304   * attribute types and values.
305   *
306   * @return  {@code true} if the server should return only attribute types, or
307   *          {@code false} if both types and values should be returned.
308   */
309  public boolean typesOnly()
310  {
311    return typesOnly;
312  }
313
314
315
316  /**
317   * Retrieves the filter for this search request.
318   *
319   * @return  The filter for this search request.
320   */
321  public Filter getFilter()
322  {
323    return filter;
324  }
325
326
327
328  /**
329   * Retrieves the set of requested attributes for this search request.
330   *
331   * @return  The set of requested attributes for this search request.
332   */
333  public List<String> getAttributes()
334  {
335    return attributes;
336  }
337
338
339
340  /**
341   * {@inheritDoc}
342   */
343  @Override()
344  public byte getProtocolOpType()
345  {
346    return LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST;
347  }
348
349
350
351  /**
352   * {@inheritDoc}
353   */
354  @Override()
355  public ASN1Element encodeProtocolOp()
356  {
357    final ArrayList<ASN1Element> attrElements =
358         new ArrayList<>(attributes.size());
359    for (final String attribute : attributes)
360    {
361      attrElements.add(new ASN1OctetString(attribute));
362    }
363
364    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST,
365         new ASN1OctetString(baseDN),
366         new ASN1Enumerated(scope.intValue()),
367         new ASN1Enumerated(derefPolicy.intValue()),
368         new ASN1Integer(sizeLimit),
369         new ASN1Integer(timeLimit),
370         new ASN1Boolean(typesOnly),
371         filter.encode(),
372         new ASN1Sequence(attrElements));
373  }
374
375
376
377  /**
378   * Decodes the provided ASN.1 element as a search request protocol op.
379   *
380   * @param  element  The ASN.1 element to be decoded.
381   *
382   * @return  The decoded search request protocol op.
383   *
384   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
385   *                         a search request protocol op.
386   */
387  public static SearchRequestProtocolOp decodeProtocolOp(
388                                             final ASN1Element element)
389         throws LDAPException
390  {
391    try
392    {
393      final ASN1Element[] elements =
394           ASN1Sequence.decodeAsSequence(element).elements();
395      final String baseDN =
396           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
397      final SearchScope scope = SearchScope.valueOf(
398           ASN1Enumerated.decodeAsEnumerated(elements[1]).intValue());
399      final DereferencePolicy derefPolicy = DereferencePolicy.valueOf(
400           ASN1Enumerated.decodeAsEnumerated(elements[2]).intValue());
401      final int sizeLimit = ASN1Integer.decodeAsInteger(elements[3]).intValue();
402      final int timeLimit = ASN1Integer.decodeAsInteger(elements[4]).intValue();
403      final boolean typesOnly =
404           ASN1Boolean.decodeAsBoolean(elements[5]).booleanValue();
405      final Filter filter = Filter.decode(elements[6]);
406
407      final ASN1Element[] attrElements =
408           ASN1Sequence.decodeAsSequence(elements[7]).elements();
409      final ArrayList<String> attributes = new ArrayList<>(attrElements.length);
410      for (final ASN1Element e : attrElements)
411      {
412        attributes.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
413      }
414
415      return new SearchRequestProtocolOp(baseDN, scope, derefPolicy, sizeLimit,
416           timeLimit, typesOnly, filter, attributes);
417    }
418    catch (final Exception e)
419    {
420      Debug.debugException(e);
421      throw new LDAPException(ResultCode.DECODING_ERROR,
422           ERR_SEARCH_REQUEST_CANNOT_DECODE.get(
423                StaticUtils.getExceptionMessage(e)),
424           e);
425    }
426  }
427
428
429
430  /**
431   * {@inheritDoc}
432   */
433  @Override()
434  public void writeTo(final ASN1Buffer buffer)
435  {
436    final ASN1BufferSequence opSequence =
437         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST);
438    buffer.addOctetString(baseDN);
439    buffer.addEnumerated(scope.intValue());
440    buffer.addEnumerated(derefPolicy.intValue());
441    buffer.addInteger(sizeLimit);
442    buffer.addInteger(timeLimit);
443    buffer.addBoolean(typesOnly);
444    filter.writeTo(buffer);
445
446    final ASN1BufferSequence attrSequence = buffer.beginSequence();
447    for (final String s : attributes)
448    {
449      buffer.addOctetString(s);
450    }
451    attrSequence.end();
452    opSequence.end();
453  }
454
455
456
457  /**
458   * Creates a search request from this protocol op.
459   *
460   * @param  controls  The set of controls to include in the search request.
461   *                   It may be empty or {@code null} if no controls should be
462   *                   included.
463   *
464   * @return  The search request that was created.
465   */
466  public SearchRequest toSearchRequest(final Control... controls)
467  {
468    final String[] attrArray = new String[attributes.size()];
469    attributes.toArray(attrArray);
470
471    return new SearchRequest(null, controls, baseDN, scope, derefPolicy,
472         sizeLimit, timeLimit, typesOnly, filter, attrArray);
473  }
474
475
476
477  /**
478   * Retrieves a string representation of this protocol op.
479   *
480   * @return  A string representation of this protocol op.
481   */
482  @Override()
483  public String toString()
484  {
485    final StringBuilder buffer = new StringBuilder();
486    toString(buffer);
487    return buffer.toString();
488  }
489
490
491
492  /**
493   * {@inheritDoc}
494   */
495  @Override()
496  public void toString(final StringBuilder buffer)
497  {
498    buffer.append("SearchRequestProtocolOp(baseDN='");
499    buffer.append(baseDN);
500    buffer.append("', scope='");
501    buffer.append(scope.toString());
502    buffer.append("', derefPolicy='");
503    buffer.append(derefPolicy.toString());
504    buffer.append("', sizeLimit=");
505    buffer.append(sizeLimit);
506    buffer.append(", timeLimit=");
507    buffer.append(timeLimit);
508    buffer.append(", typesOnly=");
509    buffer.append(typesOnly);
510    buffer.append(", filter='");
511    filter.toString(buffer);
512    buffer.append("', attributes={");
513
514    final Iterator<String> iterator = attributes.iterator();
515    while (iterator.hasNext())
516    {
517      buffer.append(iterator.next());
518      if (iterator.hasNext())
519      {
520        buffer.append(',');
521      }
522    }
523
524    buffer.append("})");
525  }
526}