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.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}