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