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