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}