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.sdk.migrate.ldapjdk;
037
038
039
040import com.unboundid.asn1.ASN1OctetString;
041import com.unboundid.ldap.sdk.AddRequest;
042import com.unboundid.ldap.sdk.AsyncRequestID;
043import com.unboundid.ldap.sdk.BindResult;
044import com.unboundid.ldap.sdk.CompareRequest;
045import com.unboundid.ldap.sdk.CompareResult;
046import com.unboundid.ldap.sdk.Control;
047import com.unboundid.ldap.sdk.DeleteRequest;
048import com.unboundid.ldap.sdk.DereferencePolicy;
049import com.unboundid.ldap.sdk.ExtendedRequest;
050import com.unboundid.ldap.sdk.ExtendedResult;
051import com.unboundid.ldap.sdk.Filter;
052import com.unboundid.ldap.sdk.InternalSDKHelper;
053import com.unboundid.ldap.sdk.LDAPConnectionOptions;
054import com.unboundid.ldap.sdk.LDAPResult;
055import com.unboundid.ldap.sdk.Modification;
056import com.unboundid.ldap.sdk.ModifyDNRequest;
057import com.unboundid.ldap.sdk.ModifyRequest;
058import com.unboundid.ldap.sdk.ResultCode;
059import com.unboundid.ldap.sdk.SearchRequest;
060import com.unboundid.ldap.sdk.SearchResult;
061import com.unboundid.ldap.sdk.SearchScope;
062import com.unboundid.ldap.sdk.SimpleBindRequest;
063import com.unboundid.ldap.sdk.UpdatableLDAPRequest;
064import com.unboundid.util.Debug;
065import com.unboundid.util.Mutable;
066import com.unboundid.util.NotExtensible;
067import com.unboundid.util.NotNull;
068import com.unboundid.util.Nullable;
069import com.unboundid.util.ThreadSafety;
070import com.unboundid.util.ThreadSafetyLevel;
071
072
073
074/**
075 * This class provides an object that may be used to communicate with an LDAP
076 * directory server.
077 * <BR><BR>
078 * This class is primarily intended to be used in the process of updating
079 * applications which use the Netscape Directory SDK for Java to switch to or
080 * coexist with the UnboundID LDAP SDK for Java.  For applications not written
081 * using the Netscape Directory SDK for Java, the
082 * {@link com.unboundid.ldap.sdk.LDAPConnection} class should be used instead.
083 */
084@Mutable()
085@NotExtensible()
086@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
087public class LDAPConnection
088{
089  /**
090   * The integer value for the DEREF_NEVER dereference policy.
091   */
092  public static final int DEREF_NEVER = DereferencePolicy.NEVER.intValue();
093
094
095
096  /**
097   * The integer value for the DEREF_SEARCHING dereference policy.
098   */
099  public static final int DEREF_SEARCHING =
100       DereferencePolicy.SEARCHING.intValue();
101
102
103
104  /**
105   * The integer value for the DEREF_FINDING dereference policy.
106   */
107  public static final int DEREF_FINDING =
108       DereferencePolicy.FINDING.intValue();
109
110
111
112  /**
113   * The integer value for the DEREF_ALWAYS dereference policy.
114   */
115  public static final int DEREF_ALWAYS =
116       DereferencePolicy.ALWAYS.intValue();
117
118
119
120  /**
121   * The integer value for the SCOPE_BASE search scope.
122   */
123  public static final int SCOPE_BASE = SearchScope.BASE_INT_VALUE;
124
125
126
127  /**
128   * The integer value for the SCOPE_ONE search scope.
129   */
130  public static final int SCOPE_ONE = SearchScope.ONE_INT_VALUE;
131
132
133
134  /**
135   * The integer value for the SCOPE_SUB search scope.
136   */
137  public static final int SCOPE_SUB = SearchScope.SUB_INT_VALUE;
138
139
140
141  // The connection used to perform the actual communication with the server.
142  @NotNull private volatile com.unboundid.ldap.sdk.LDAPConnection conn;
143
144  // The default constraints that will be used for non-search operations.
145  @NotNull private LDAPConstraints constraints;
146
147  // The set of controls returned from the last operation.
148  @Nullable private LDAPControl[] responseControls;
149
150  // The default constraints that will be used for search operations.
151  @NotNull private LDAPSearchConstraints searchConstraints;
152
153  // The socket factory for this connection.
154  @Nullable private LDAPSocketFactory socketFactory;
155
156  // The DN last used to bind to the server.
157  @Nullable private String authDN;
158
159  // The password last used to bind to the server.
160  @Nullable private String authPW;
161
162
163
164  /**
165   * Creates a new LDAP connection which will use the default socket factory.
166   */
167  public LDAPConnection()
168  {
169    this(null);
170  }
171
172
173
174  /**
175   * Creates a new LDAP connection which will use the provided socket factory.
176   *
177   * @param  socketFactory  The socket factory to use when creating the socket
178   *                        to use for communicating with the server.
179   */
180  public LDAPConnection(@Nullable final LDAPSocketFactory socketFactory)
181  {
182    this.socketFactory = socketFactory;
183    if (socketFactory == null)
184    {
185      conn = new com.unboundid.ldap.sdk.LDAPConnection();
186    }
187    else
188    {
189
190      conn = new com.unboundid.ldap.sdk.LDAPConnection(
191           new LDAPToJavaSocketFactory(socketFactory));
192    }
193
194    authDN = null;
195    authPW = null;
196
197    constraints       = new LDAPConstraints();
198    searchConstraints = new LDAPSearchConstraints();
199  }
200
201
202
203  /**
204   * Closes the connection to the server if the client forgets to do so.
205   *
206   * @throws  Throwable  If a problem occurs.
207   */
208  @Override()
209  protected void finalize()
210            throws Throwable
211  {
212    conn.close();
213
214    super.finalize();
215  }
216
217
218
219  /**
220   * Retrieves the {@link com.unboundid.ldap.sdk.LDAPConnection} object used to
221   * back this connection.
222   *
223   * @return  The {@code com.unboundid.ldap.sdk.LDAPConnection} object used to
224   *          back this connection.
225   */
226  @NotNull()
227  public com.unboundid.ldap.sdk.LDAPConnection getSDKConnection()
228  {
229    return conn;
230  }
231
232
233
234  /**
235   * Retrieves the address to which the connection is established.
236   *
237   * @return  The address to which the connection is established.
238   */
239  @Nullable()
240  public String getHost()
241  {
242    return conn.getConnectedAddress();
243  }
244
245
246
247  /**
248   * Retrieves the port to which the connection is established.
249   *
250   * @return  The port to which the connection is established.
251   */
252  public int getPort()
253  {
254    return conn.getConnectedPort();
255  }
256
257
258
259  /**
260   * Retrieves the DN of the user that last authenticated on this connection.
261   *
262   * @return  The DN of the user that last authenticated on this connection,
263   *          or {@code null} if it is not available.
264   */
265  @Nullable()
266  public String getAuthenticationDN()
267  {
268    return authDN;
269  }
270
271
272
273  /**
274   * Retrieves the password of the user that last authenticated on this
275   * connection.
276   *
277   * @return  The password of the user that last authenticated on this
278   *           connection, or {@code null} if it is not available.
279   */
280  @Nullable()
281  public String getAuthenticationPassword()
282  {
283    return authPW;
284  }
285
286
287
288  /**
289   * Retrieves the maximum length of time to wait for the connection to be
290   * established, in seconds.
291   *
292   * @return  The maximum length of time to wait for the connection to be
293   *          established.
294   */
295  public int getConnectTimeout()
296  {
297    final int connectTimeoutMillis =
298         conn.getConnectionOptions().getConnectTimeoutMillis();
299    if (connectTimeoutMillis > 0)
300    {
301      return Math.max(1, (connectTimeoutMillis / 1000));
302    }
303    else
304    {
305      return 0;
306    }
307  }
308
309
310
311  /**
312   * Specifies the maximum length of time to wait for the connection to be
313   * established, in seconds.
314   *
315   * @param  timeout  The maximum length of time to wait for the connection to
316   *                  be established.
317   */
318  public void setConnectTimeout(final int timeout)
319  {
320    final LDAPConnectionOptions options = conn.getConnectionOptions();
321
322    if (timeout > 0)
323    {
324      options.setConnectTimeoutMillis(1000 * timeout);
325    }
326    else
327    {
328      options.setConnectTimeoutMillis(0);
329    }
330
331    conn.setConnectionOptions(options);
332  }
333
334
335
336  /**
337   * Retrieves the socket factory for this LDAP connection, if specified.
338   *
339   * @return  The socket factory for this LDAP connection, or {@code null} if
340   *          none has been provided.
341   */
342  @Nullable()
343  public LDAPSocketFactory getSocketFactory()
344  {
345    return socketFactory;
346  }
347
348
349
350  /**
351   * Sets the socket factory for this LDAP connection.
352   *
353   * @param  socketFactory  The socket factory for this LDAP connection.
354   */
355  public void setSocketFactory(@Nullable final LDAPSocketFactory socketFactory)
356  {
357    this.socketFactory = socketFactory;
358
359    if (socketFactory == null)
360    {
361      conn.setSocketFactory(null);
362    }
363    else
364    {
365      conn.setSocketFactory(new LDAPToJavaSocketFactory(socketFactory));
366    }
367  }
368
369
370
371  /**
372   * Retrieves the constraints for this connection.
373   *
374   * @return  The constraints for this connection.
375   */
376  @NotNull()
377  public LDAPConstraints getConstraints()
378  {
379    return constraints;
380  }
381
382
383
384  /**
385   * Updates the constraints for this connection.
386   *
387   * @param  constraints  The constraints for this connection.
388   */
389  public void setConstraints(@Nullable final LDAPConstraints constraints)
390  {
391    if (constraints == null)
392    {
393      this.constraints = new LDAPConstraints();
394    }
395    else
396    {
397      this.constraints = constraints;
398    }
399  }
400
401
402
403  /**
404   * Retrieves the search constraints for this connection.
405   *
406   * @return  The search constraints for this connection.
407   */
408  @NotNull()
409  public LDAPSearchConstraints getSearchConstraints()
410  {
411    return searchConstraints;
412  }
413
414
415
416  /**
417   * Updates the search constraints for this connection.
418   *
419   * @param  searchConstraints  The search constraints for this connection.
420   */
421  public void setSearchConstraints(
422                   @Nullable final LDAPSearchConstraints searchConstraints)
423  {
424    if (searchConstraints == null)
425    {
426      this.searchConstraints = new LDAPSearchConstraints();
427    }
428    else
429    {
430      this.searchConstraints = searchConstraints;
431    }
432  }
433
434
435
436  /**
437   * Retrieves the response controls from the last operation processed on this
438   * connection.
439   *
440   * @return  The response controls from the last operation processed on this
441   *          connection, or {@code null} if there were none.
442   */
443  @Nullable()
444  public LDAPControl[] getResponseControls()
445  {
446    return responseControls;
447  }
448
449
450
451  /**
452   * Indicates whether this connection is currently established.
453   *
454   * @return  {@code true} if this connection is currently established, or
455   *          {@code false} if not.
456   */
457  public boolean isConnected()
458  {
459    return conn.isConnected();
460  }
461
462
463
464  /**
465   * Attempts to establish this connection with the provided information.
466   *
467   * @param  host  The address of the server to which the connection should be
468   *               established.
469   * @param  port  The port of the server to which the connection should be
470   *               established.
471   *
472   * @throws  LDAPException  If a problem occurs while attempting to establish
473   *                         this connection.
474   */
475  public void connect(@NotNull final String host, final int port)
476         throws LDAPException
477  {
478    authDN           = null;
479    authPW           = null;
480    responseControls = null;
481
482    try
483    {
484      conn.close();
485      if (socketFactory == null)
486      {
487        conn = new com.unboundid.ldap.sdk.LDAPConnection(host, port);
488      }
489      else
490      {
491
492        conn = new com.unboundid.ldap.sdk.LDAPConnection(
493             new LDAPToJavaSocketFactory(socketFactory), host, port);
494      }
495    }
496    catch (final com.unboundid.ldap.sdk.LDAPException le)
497    {
498      Debug.debugException(le);
499      throw new LDAPException(le);
500    }
501  }
502
503
504
505  /**
506   * Attempts to establish and authenticate this connection with the provided
507   * information.
508   *
509   * @param  host      The address of the server to which the connection should
510   *                   be established.
511   * @param  port      The port of the server to which the connection should be
512   *                   established.
513   * @param  dn        The DN to use to bind to the server.
514   * @param  password  The password to use to bind to the server.
515   *
516   * @throws  LDAPException  If a problem occurs while attempting to establish
517   *                         or authenticate this connection.  If an exception
518   *                         is thrown, then the connection will not be
519   *                         established.
520   */
521  public void connect(@NotNull final String host, final int port,
522                      @Nullable final String dn,
523                      @Nullable final String password)
524         throws LDAPException
525  {
526    connect(3, host, port, dn, password, null);
527  }
528
529
530
531  /**
532   * Attempts to establish and authenticate this connection with the provided
533   * information.
534   *
535   * @param  host         The address of the server to which the connection
536   *                      should be established.
537   * @param  port         The port of the server to which the connection should
538   *                      be established.
539   * @param  dn           The DN to use to bind to the server.
540   * @param  password     The password to use to bind to the server.
541   * @param  constraints  The constraints to use when processing the bind.
542   *
543   * @throws  LDAPException  If a problem occurs while attempting to establish
544   *                         or authenticate this connection.  If an exception
545   *                         is thrown, then the connection will not be
546   *                         established.
547   */
548  public void connect(@NotNull final String host, final int port,
549                      @Nullable final String dn,
550                      @Nullable final String password,
551                      @Nullable final LDAPConstraints constraints)
552         throws LDAPException
553  {
554    connect(3, host, port, dn, password, constraints);
555  }
556
557
558
559  /**
560   * Attempts to establish and authenticate this connection with the provided
561   * information.
562   *
563   * @param  version   The LDAP protocol version to use for the connection.
564   *                   This will be ignored, since this implementation only
565   *                   supports LDAPv3.
566   * @param  host      The address of the server to which the connection should
567   *                   be established.
568   * @param  port      The port of the server to which the connection should be
569   *                   established.
570   * @param  dn        The DN to use to bind to the server.
571   * @param  password  The password to use to bind to the server.
572   *
573   * @throws  LDAPException  If a problem occurs while attempting to establish
574   *                         or authenticate this connection.  If an exception
575   *                         is thrown, then the connection will not be
576   *                         established.
577   */
578  public void connect(final int version, @NotNull final String host,
579                      final int port, @Nullable final String dn,
580                      @Nullable final String password)
581         throws LDAPException
582  {
583    connect(version, host, port, dn, password, null);
584  }
585
586
587
588  /**
589   * Attempts to establish and authenticate this connection with the provided
590   * information.
591   *
592   * @param  version      The LDAP protocol version to use for the connection.
593   *                      This will be ignored, since this implementation only
594   *                      supports LDAPv3.
595   * @param  host         The address of the server to which the connection
596   *                      should be established.
597   * @param  port         The port of the server to which the connection should
598   *                      be established.
599   * @param  dn           The DN to use to bind to the server.
600   * @param  password     The password to use to bind to the server.
601   * @param  constraints  The constraints to use when processing the bind.
602   *
603   * @throws  LDAPException  If a problem occurs while attempting to establish
604   *                         or authenticate this connection.  If an exception
605   *                         is thrown, then the connection will not be
606   *                         established.
607   */
608  public void connect(final int version, @NotNull final String host,
609                      final int port, @Nullable final String dn,
610                      @Nullable final String password,
611                      @Nullable final LDAPConstraints constraints)
612         throws LDAPException
613  {
614    connect(host, port);
615
616    try
617    {
618      if ((dn != null) && (password != null))
619      {
620        bind(version, dn, password, constraints);
621      }
622    }
623    catch (final LDAPException le)
624    {
625      conn.close();
626      throw le;
627    }
628  }
629
630
631
632  /**
633   * Unbinds and disconnects from the directory server.
634   *
635   * @throws  LDAPException  If a problem occurs.
636   */
637  public void disconnect()
638         throws LDAPException
639  {
640    authDN = null;
641    authPW = null;
642
643    conn.close();
644    if (socketFactory == null)
645    {
646      conn = new com.unboundid.ldap.sdk.LDAPConnection();
647    }
648    else
649    {
650
651      conn = new com.unboundid.ldap.sdk.LDAPConnection(
652           new LDAPToJavaSocketFactory(socketFactory));
653    }
654  }
655
656
657
658  /**
659   * Disconnects from the directory server and attempts to re-connect and
660   * re-authenticate.
661   *
662   * @throws  LDAPException  If a problem occurs.  If an exception is thrown,
663   *                         the connection will have been closed.
664   */
665  public void reconnect()
666         throws LDAPException
667  {
668    final String host = getHost();
669    final int    port = getPort();
670    final String dn   = authDN;
671    final String pw   = authPW;
672
673    if ((dn == null) || (pw == null))
674    {
675      connect(host, port);
676    }
677    else
678    {
679      connect(host, port, dn, pw);
680    }
681  }
682
683
684
685  /**
686   * Sends a request to abandon the request with the specified message ID.
687   *
688   * @param  id  The message ID of the operation to abandon.
689   *
690   * @throws  LDAPException  If a problem occurs while sending the request.
691   */
692  public void abandon(final int id)
693         throws LDAPException
694  {
695    try
696    {
697      conn.abandon(InternalSDKHelper.createAsyncRequestID(id, conn),
698                   getControls(null));
699    }
700    catch (final com.unboundid.ldap.sdk.LDAPException le)
701    {
702      Debug.debugException(le);
703      throw new LDAPException(le);
704    }
705  }
706
707
708
709  /**
710   * Sends a request to abandon the provided search operation.
711   *
712   * @param  searchResults  The search results object for the search to abandon.
713   *
714   * @throws  LDAPException  If a problem occurs while sending the request.
715   */
716  public void abandon(@NotNull final LDAPSearchResults searchResults)
717         throws LDAPException
718  {
719    try
720    {
721      final AsyncRequestID requestID = searchResults.getAsyncRequestID();
722      if (requestID != null)
723      {
724        searchResults.setAbandoned();
725        conn.abandon(requestID);
726      }
727      else
728      {
729        // This should never happen.
730        throw new LDAPException(
731             "The search request has not been sent to the server",
732             LDAPException.PARAM_ERROR);
733      }
734    }
735    catch (final com.unboundid.ldap.sdk.LDAPException le)
736    {
737      Debug.debugException(le);
738      throw new LDAPException(le);
739    }
740  }
741
742
743
744  /**
745   * Adds the provided entry to the directory.
746   *
747   * @param  entry  The entry to be added.
748   *
749   * @throws  LDAPException  If a problem occurs while adding the entry.
750   */
751  public void add(@NotNull final LDAPEntry entry)
752         throws LDAPException
753  {
754    add(entry, null);
755  }
756
757
758
759  /**
760   * Adds the provided entry to the directory.
761   *
762   * @param  entry        The entry to be added.
763   * @param  constraints  The constraints to use for the add operation.
764   *
765   * @throws  LDAPException  If a problem occurs while adding the entry.
766   */
767  public void add(@NotNull final LDAPEntry entry,
768                  @Nullable final LDAPConstraints constraints)
769         throws LDAPException
770  {
771    final AddRequest addRequest = new AddRequest(entry.toEntry());
772    update(addRequest, constraints);
773
774    try
775    {
776      final LDAPResult result = conn.add(addRequest);
777      setResponseControls(result);
778    }
779    catch (final com.unboundid.ldap.sdk.LDAPException le)
780    {
781      Debug.debugException(le);
782      setResponseControls(le);
783      throw new LDAPException(le);
784    }
785  }
786
787
788
789  /**
790   * Authenticates to the directory server using a simple bind with the provided
791   * information.
792   *
793   * @param  dn        The DN of the user for the bind.
794   * @param  password  The password to use for the bind.
795   *
796   * @throws  LDAPException  If the bind attempt fails.
797   */
798  public void authenticate(@Nullable final String dn,
799                           @Nullable final String password)
800         throws LDAPException
801  {
802    bind(3, dn, password, null);
803  }
804
805
806
807  /**
808   * Authenticates to the directory server using a simple bind with the provided
809   * information.
810   *
811   * @param  dn           The DN of the user for the bind.
812   * @param  password     The password to use for the bind.
813   * @param  constraints  The constraints to use for the bind operation.
814   *
815   * @throws  LDAPException  If the bind attempt fails.
816   */
817  public void authenticate(@Nullable final String dn,
818                           @Nullable final String password,
819                           @Nullable final LDAPConstraints constraints)
820         throws LDAPException
821  {
822    bind(3, dn, password, constraints);
823  }
824
825
826
827  /**
828   * Authenticates to the directory server using a simple bind with the provided
829   * information.
830   *
831   * @param  version   The LDAP protocol version to use.  This will be ignored,
832   *                   since this implementation only supports LDAPv3.
833   * @param  dn        The DN of the user for the bind.
834   * @param  password  The password to use for the bind.
835   *
836   * @throws  LDAPException  If the bind attempt fails.
837   */
838  public void authenticate(final int version, @Nullable final String dn,
839                           @Nullable final String password)
840         throws LDAPException
841  {
842    bind(version, dn, password, null);
843  }
844
845
846
847  /**
848   * Authenticates to the directory server using a simple bind with the provided
849   * information.
850   *
851   * @param  version      The LDAP protocol version to use.  This will be
852   *                      ignored, since this implementation only supports
853   *                      LDAPv3.
854   * @param  dn           The DN of the user for the bind.
855   * @param  password     The password to use for the bind.
856   * @param  constraints  The constraints to use for the bind operation.
857   *
858   * @throws  LDAPException  If the bind attempt fails.
859   */
860  public void authenticate(final int version, @Nullable final String dn,
861                           @Nullable final String password,
862                           @Nullable final LDAPConstraints constraints)
863         throws LDAPException
864  {
865    bind(version, dn, password, constraints);
866  }
867
868
869
870  /**
871   * Authenticates to the directory server using a simple bind with the provided
872   * information.
873   *
874   * @param  dn        The DN of the user for the bind.
875   * @param  password  The password to use for the bind.
876   *
877   * @throws  LDAPException  If the bind attempt fails.
878   */
879  public void bind(@Nullable final String dn, @Nullable final String password)
880         throws LDAPException
881  {
882    bind(3, dn, password, null);
883  }
884
885
886
887  /**
888   * Authenticates to the directory server using a simple bind with the provided
889   * information.
890   *
891   * @param  dn           The DN of the user for the bind.
892   * @param  password     The password to use for the bind.
893   * @param  constraints  The constraints to use for the bind operation.
894   *
895   * @throws  LDAPException  If the bind attempt fails.
896   */
897  public void bind(@Nullable final String dn, @Nullable final String password,
898                   @Nullable final LDAPConstraints constraints)
899         throws LDAPException
900  {
901    bind(3, dn, password, constraints);
902  }
903
904
905
906  /**
907   * Authenticates to the directory server using a simple bind with the provided
908   * information.
909   *
910   * @param  version   The LDAP protocol version to use.  This will be ignored,
911   *                   since this implementation only supports LDAPv3.
912   * @param  dn        The DN of the user for the bind.
913   * @param  password  The password to use for the bind.
914   *
915   * @throws  LDAPException  If the bind attempt fails.
916   */
917  public void bind(final int version, @Nullable final String dn,
918                   @Nullable final String password)
919         throws LDAPException
920  {
921    bind(version, dn, password, null);
922  }
923
924
925
926  /**
927   * Authenticates to the directory server using a simple bind with the provided
928   * information.
929   *
930   * @param  version      The LDAP protocol version to use.  This will be
931   *                      ignored, since this implementation only supports
932   *                      LDAPv3.
933   * @param  dn           The DN of the user for the bind.
934   * @param  password     The password to use for the bind.
935   * @param  constraints  The constraints to use for the bind operation.
936   *
937   * @throws  LDAPException  If the bind attempt fails.
938   */
939  public void bind(final int version, @Nullable final String dn,
940                   @Nullable final String password,
941                   @Nullable final LDAPConstraints constraints)
942         throws LDAPException
943  {
944    final SimpleBindRequest bindRequest =
945         new SimpleBindRequest(dn, password, getControls(constraints));
946    authDN = null;
947    authPW = null;
948
949    try
950    {
951      final BindResult bindResult = conn.bind(bindRequest);
952      setResponseControls(bindResult);
953      if (bindResult.getResultCode() == ResultCode.SUCCESS)
954      {
955        authDN = dn;
956        authPW = password;
957      }
958    }
959    catch (final com.unboundid.ldap.sdk.LDAPException le)
960    {
961      Debug.debugException(le);
962      setResponseControls(le);
963      throw new LDAPException(le);
964    }
965  }
966
967
968
969  /**
970   * Indicates whether the specified entry has the given attribute value.
971   *
972   * @param  dn         The DN of the entry to compare.
973   * @param  attribute  The attribute (which must have exactly one value) to use
974   *                    for the comparison.
975   *
976   * @return  {@code true} if the compare matched the target entry, or
977   *          {@code false} if not.
978   *
979   * @throws  LDAPException  If a problem occurs while processing the compare.
980   */
981  public boolean compare(@NotNull final String dn,
982                         @NotNull final LDAPAttribute attribute)
983         throws LDAPException
984  {
985    return compare(dn, attribute, null);
986  }
987
988
989
990  /**
991   * Indicates whether the specified entry has the given attribute value.
992   *
993   * @param  dn           The DN of the entry to compare.
994   * @param  attribute    The attribute (which must have exactly one value) to
995   *                      use for the comparison.
996   * @param  constraints  The constraints to use for the compare operation.
997   *
998   * @return  {@code true} if the compare matched the target entry, or
999   *          {@code false} if not.
1000   *
1001   * @throws  LDAPException  If a problem occurs while processing the compare.
1002   */
1003  public boolean compare(@NotNull final String dn,
1004                         @NotNull final LDAPAttribute attribute,
1005                         @Nullable final LDAPConstraints constraints)
1006         throws LDAPException
1007  {
1008    final CompareRequest compareRequest = new CompareRequest(dn,
1009         attribute.getName(), attribute.getByteValueArray()[0]);
1010    update(compareRequest, constraints);
1011
1012    try
1013    {
1014      final CompareResult result = conn.compare(compareRequest);
1015      setResponseControls(result);
1016      return result.compareMatched();
1017    }
1018    catch (final com.unboundid.ldap.sdk.LDAPException le)
1019    {
1020      Debug.debugException(le);
1021      setResponseControls(le);
1022      throw new LDAPException(le);
1023    }
1024  }
1025
1026
1027
1028  /**
1029   * Removes an entry from the directory.
1030   *
1031   * @param  dn  The DN of the entry to delete.
1032   *
1033   * @throws  LDAPException  If a problem occurs while processing the delete.
1034   */
1035  public void delete(@NotNull final String dn)
1036         throws LDAPException
1037  {
1038    delete(dn, null);
1039  }
1040
1041
1042
1043  /**
1044   * Removes an entry from the directory.
1045   *
1046   * @param  dn           The DN of the entry to delete.
1047   * @param  constraints  The constraints to use for the delete operation.
1048   *
1049   * @throws  LDAPException  If a problem occurs while processing the delete.
1050   */
1051  public void delete(@NotNull final String dn,
1052                     @Nullable final LDAPConstraints constraints)
1053         throws LDAPException
1054  {
1055    final DeleteRequest deleteRequest = new DeleteRequest(dn);
1056    update(deleteRequest, constraints);
1057
1058    try
1059    {
1060      final LDAPResult result = conn.delete(deleteRequest);
1061      setResponseControls(result);
1062    }
1063    catch (final com.unboundid.ldap.sdk.LDAPException le)
1064    {
1065      Debug.debugException(le);
1066      setResponseControls(le);
1067      throw new LDAPException(le);
1068    }
1069  }
1070
1071
1072
1073  /**
1074   * Processes an extended operation in the directory.
1075   *
1076   * @param  extendedOperation  The extended operation to process.
1077   *
1078   * @return  The result returned from the extended operation.
1079   *
1080   * @throws  LDAPException  If a problem occurs while processing the operation.
1081   */
1082  @NotNull()
1083  public LDAPExtendedOperation extendedOperation(
1084              @NotNull final LDAPExtendedOperation extendedOperation)
1085         throws LDAPException
1086  {
1087    return extendedOperation(extendedOperation,  null);
1088  }
1089
1090
1091
1092  /**
1093   * Processes an extended operation in the directory.
1094   *
1095   * @param  extendedOperation  The extended operation to process.
1096   * @param  constraints        The constraints to use for the operation.
1097   *
1098   * @return  The result returned from the extended operation.
1099   *
1100   * @throws  LDAPException  If a problem occurs while processing the operation.
1101   */
1102  @NotNull()
1103  public LDAPExtendedOperation extendedOperation(
1104              @NotNull final LDAPExtendedOperation extendedOperation,
1105              @Nullable final LDAPConstraints constraints)
1106         throws LDAPException
1107  {
1108    final ExtendedRequest extendedRequest = new ExtendedRequest(
1109         extendedOperation.getID(),
1110         new ASN1OctetString(extendedOperation.getValue()),
1111         getControls(constraints));
1112
1113    try
1114    {
1115      final ExtendedResult result =
1116           conn.processExtendedOperation(extendedRequest);
1117      setResponseControls(result);
1118
1119      if (result.getResultCode() != ResultCode.SUCCESS)
1120      {
1121        throw new LDAPException(result.getDiagnosticMessage(),
1122             result.getResultCode().intValue(), result.getDiagnosticMessage(),
1123             result.getMatchedDN());
1124      }
1125
1126      final byte[] valueBytes;
1127      final ASN1OctetString value = result.getValue();
1128      if (value == null)
1129      {
1130        valueBytes = null;
1131      }
1132      else
1133      {
1134        valueBytes = value.getValue();
1135      }
1136
1137      return new LDAPExtendedOperation(result.getOID(), valueBytes);
1138    }
1139    catch (final com.unboundid.ldap.sdk.LDAPException le)
1140    {
1141      Debug.debugException(le);
1142      setResponseControls(le);
1143      throw new LDAPException(le);
1144    }
1145  }
1146
1147
1148
1149  /**
1150   * Modifies an entry in the directory.
1151   *
1152   * @param  dn   The DN of the entry to modify.
1153   * @param  mod  The modification to apply to the entry.
1154   *
1155   * @throws  LDAPException  If a problem occurs while processing the delete.
1156   */
1157  public void modify(@NotNull final String dn,
1158                     @NotNull final LDAPModification mod)
1159         throws LDAPException
1160  {
1161    modify(dn, new LDAPModification[] { mod }, null);
1162  }
1163
1164
1165
1166  /**
1167   * Modifies an entry in the directory.
1168   *
1169   * @param  dn    The DN of the entry to modify.
1170   * @param  mods  The modifications to apply to the entry.
1171   *
1172   * @throws  LDAPException  If a problem occurs while processing the delete.
1173   */
1174  public void modify(@NotNull final String dn,
1175                     @NotNull final LDAPModification[] mods)
1176         throws LDAPException
1177  {
1178    modify(dn, mods, null);
1179  }
1180
1181
1182
1183  /**
1184   * Modifies an entry in the directory.
1185   *
1186   * @param  dn           The DN of the entry to modify.
1187   * @param  mod          The modification to apply to the entry.
1188   * @param  constraints  The constraints to use for the modify operation.
1189   *
1190   * @throws  LDAPException  If a problem occurs while processing the delete.
1191   */
1192  public void modify(@NotNull final String dn,
1193                     @NotNull final LDAPModification mod,
1194                     @Nullable final LDAPConstraints constraints)
1195         throws LDAPException
1196  {
1197    modify(dn, new LDAPModification[] { mod }, constraints);
1198  }
1199
1200
1201
1202  /**
1203   * Modifies an entry in the directory.
1204   *
1205   * @param  dn           The DN of the entry to modify.
1206   * @param  mods         The modifications to apply to the entry.
1207   * @param  constraints  The constraints to use for the modify operation.
1208   *
1209   * @throws  LDAPException  If a problem occurs while processing the delete.
1210   */
1211  public void modify(@NotNull final String dn,
1212                     @NotNull final LDAPModification[] mods,
1213                     @Nullable final LDAPConstraints constraints)
1214         throws LDAPException
1215  {
1216    final Modification[] m = new Modification[mods.length];
1217    for (int i=0; i < mods.length; i++)
1218    {
1219      m[i] = mods[i].toModification();
1220    }
1221
1222    final ModifyRequest modifyRequest = new ModifyRequest(dn, m);
1223    update(modifyRequest, constraints);
1224
1225    try
1226    {
1227      final LDAPResult result = conn.modify(modifyRequest);
1228      setResponseControls(result);
1229    }
1230    catch (final com.unboundid.ldap.sdk.LDAPException le)
1231    {
1232      Debug.debugException(le);
1233      setResponseControls(le);
1234      throw new LDAPException(le);
1235    }
1236  }
1237
1238
1239
1240  /**
1241   * Modifies an entry in the directory.
1242   *
1243   * @param  dn    The DN of the entry to modify.
1244   * @param  mods  The modifications to apply to the entry.
1245   *
1246   * @throws  LDAPException  If a problem occurs while processing the delete.
1247   */
1248  public void modify(@NotNull final String dn,
1249                     @NotNull final LDAPModificationSet mods)
1250         throws LDAPException
1251  {
1252    modify(dn, mods.toArray(), null);
1253  }
1254
1255
1256
1257  /**
1258   * Modifies an entry in the directory.
1259   *
1260   * @param  dn           The DN of the entry to modify.
1261   * @param  mods         The modifications to apply to the entry.
1262   * @param  constraints  The constraints to use for the modify operation.
1263   *
1264   * @throws  LDAPException  If a problem occurs while processing the delete.
1265   */
1266  public void modify(@NotNull final String dn,
1267                     @NotNull final LDAPModificationSet mods,
1268                     @Nullable final LDAPConstraints constraints)
1269         throws LDAPException
1270  {
1271    modify(dn, mods.toArray(), constraints);
1272  }
1273
1274
1275
1276  /**
1277   * Retrieves an entry from the directory server.
1278   *
1279   * @param  dn  The DN of the entry to retrieve.
1280   *
1281   * @return  The entry that was read.
1282   *
1283   * @throws  LDAPException  If a problem occurs while performing the search.
1284   */
1285  @NotNull()
1286  public LDAPEntry read(@NotNull final String dn)
1287         throws LDAPException
1288  {
1289    return read(dn, null, null);
1290  }
1291
1292
1293
1294  /**
1295   * Retrieves an entry from the directory server.
1296   *
1297   * @param  dn           The DN of the entry to retrieve.
1298   * @param  constraints  The constraints to use for the search operation.
1299   *
1300   * @return  The entry that was read.
1301   *
1302   * @throws  LDAPException  If a problem occurs while performing the search.
1303   */
1304  @NotNull()
1305  public LDAPEntry read(@NotNull final String dn,
1306                        @Nullable final LDAPSearchConstraints constraints)
1307         throws LDAPException
1308  {
1309    return read(dn, null, constraints);
1310  }
1311
1312
1313
1314  /**
1315   * Retrieves an entry from the directory server.
1316   *
1317   * @param  dn     The DN of the entry to retrieve.
1318   * @param  attrs  The set of attributes to request.
1319   *
1320   * @return  The entry that was read.
1321   *
1322   * @throws  LDAPException  If a problem occurs while performing the search.
1323   */
1324  @NotNull()
1325  public LDAPEntry read(@NotNull final String dn,
1326                        @Nullable final String[] attrs)
1327         throws LDAPException
1328  {
1329    return read(dn, attrs, null);
1330  }
1331
1332
1333
1334  /**
1335   * Retrieves an entry from the directory server.
1336   *
1337   * @param  dn           The DN of the entry to retrieve.
1338   * @param  attrs        The set of attributes to request.
1339   * @param  constraints  The constraints to use for the search operation.
1340   *
1341   * @return  The entry that was read.
1342   *
1343   * @throws  LDAPException  If a problem occurs while performing the search.
1344   */
1345  @NotNull()
1346  public LDAPEntry read(@NotNull final String dn,
1347                        @Nullable final String[] attrs,
1348                        @Nullable final LDAPSearchConstraints constraints)
1349         throws LDAPException
1350  {
1351    final Filter filter = Filter.createORFilter(
1352         Filter.createPresenceFilter("objectClass"),
1353         Filter.createEqualityFilter("objectClass", "ldapSubentry"));
1354
1355    final SearchRequest searchRequest =
1356         new SearchRequest(dn, SearchScope.BASE, filter, attrs);
1357    update(searchRequest, constraints);
1358
1359    try
1360    {
1361      final SearchResult searchResult = conn.search(searchRequest);
1362      setResponseControls(searchResult);
1363
1364      if (searchResult.getEntryCount() != 1)
1365      {
1366        throw new LDAPException(null, LDAPException.NO_RESULTS_RETURNED);
1367      }
1368
1369      return new LDAPEntry(searchResult.getSearchEntries().get(0));
1370    }
1371    catch (final com.unboundid.ldap.sdk.LDAPException le)
1372    {
1373      Debug.debugException(le);
1374      setResponseControls(le);
1375      throw new LDAPException(le);
1376    }
1377  }
1378
1379
1380
1381  /**
1382   * Alters the DN of an entry in the directory.
1383   *
1384   * @param  dn            The DN of the entry to modify.
1385   * @param  newRDN        The new RDN to use for the entry.
1386   * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1387   *
1388   * @throws  LDAPException  If a problem occurs while processing the delete.
1389   */
1390  public void rename(@NotNull final String dn, @NotNull final String newRDN,
1391                     final boolean deleteOldRDN)
1392         throws LDAPException
1393  {
1394    rename(dn, newRDN, null, deleteOldRDN, null);
1395  }
1396
1397
1398
1399  /**
1400   * Alters the DN of an entry in the directory.
1401   *
1402   * @param  dn            The DN of the entry to modify.
1403   * @param  newRDN        The new RDN to use for the entry.
1404   * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1405   * @param  constraints   The constraints to use for the modify operation.
1406   *
1407   * @throws  LDAPException  If a problem occurs while processing the delete.
1408   */
1409  public void rename(@NotNull final String dn, @NotNull final String newRDN,
1410                     final boolean deleteOldRDN,
1411                     @Nullable final LDAPConstraints constraints)
1412         throws LDAPException
1413  {
1414    rename(dn, newRDN, null, deleteOldRDN, constraints);
1415  }
1416
1417
1418
1419  /**
1420   * Alters the DN of an entry in the directory.
1421   *
1422   * @param  dn            The DN of the entry to modify.
1423   * @param  newRDN        The new RDN to use for the entry.
1424   * @param  newParentDN   The DN of the new parent, or {@code null} if it
1425   *                       should not be moved below a new parent.
1426   * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1427   *
1428   * @throws  LDAPException  If a problem occurs while processing the delete.
1429   */
1430  public void rename(@NotNull final String dn, @NotNull final String newRDN,
1431                     @Nullable final String newParentDN,
1432                     final boolean deleteOldRDN)
1433         throws LDAPException
1434  {
1435    rename(dn, newRDN, newParentDN, deleteOldRDN, null);
1436  }
1437
1438
1439
1440  /**
1441   * Alters the DN of an entry in the directory.
1442   *
1443   * @param  dn            The DN of the entry to modify.
1444   * @param  newRDN        The new RDN to use for the entry.
1445   * @param  newParentDN   The DN of the new parent, or {@code null} if it
1446   *                       should not be moved below a new parent.
1447   * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1448   * @param  constraints   The constraints to use for the modify operation.
1449   *
1450   * @throws  LDAPException  If a problem occurs while processing the delete.
1451   */
1452  public void rename(@NotNull final String dn, @NotNull final String newRDN,
1453                     @Nullable final String newParentDN,
1454                     final boolean deleteOldRDN,
1455                     @Nullable final LDAPConstraints constraints)
1456         throws LDAPException
1457  {
1458    final ModifyDNRequest modifyDNRequest =
1459         new ModifyDNRequest(dn, newRDN, deleteOldRDN, newParentDN);
1460    update(modifyDNRequest, constraints);
1461
1462    try
1463    {
1464      final LDAPResult result = conn.modifyDN(modifyDNRequest);
1465      setResponseControls(result);
1466    }
1467    catch (final com.unboundid.ldap.sdk.LDAPException le)
1468    {
1469      Debug.debugException(le);
1470      setResponseControls(le);
1471      throw new LDAPException(le);
1472    }
1473  }
1474
1475
1476
1477  /**
1478   * Processes a search in the directory server.
1479   *
1480   * @param  baseDN       The base DN for the search.
1481   * @param  scope        The scope for the search.
1482   * @param  filter       The filter for the search.
1483   * @param  attributes   The set of attributes to request.
1484   * @param  typesOnly    Indicates whether to return attribute types only or
1485   *                      both types and values.
1486   *
1487   * @return  The entry that was read.
1488   *
1489   * @throws  LDAPException  If a problem occurs while performing the search.
1490   */
1491  @NotNull()
1492  public LDAPSearchResults search(@NotNull final String baseDN, final int scope,
1493              @NotNull final String filter,
1494              @Nullable final String[] attributes,
1495              final boolean typesOnly)
1496         throws LDAPException
1497  {
1498    return search(baseDN, scope, filter, attributes, typesOnly, null);
1499  }
1500
1501
1502
1503  /**
1504   * Processes a search in the directory server.
1505   *
1506   * @param  baseDN       The base DN for the search.
1507   * @param  scope        The scope for the search.
1508   * @param  filter       The filter for the search.
1509   * @param  attributes   The set of attributes to request.
1510   * @param  typesOnly    Indicates whether to return attribute types only or
1511   *                      both types and values.
1512   * @param  constraints  The constraints to use for the search operation.
1513   *
1514   * @return  The entry that was read.
1515   *
1516   * @throws  LDAPException  If a problem occurs while performing the search.
1517   */
1518  @NotNull()
1519  public LDAPSearchResults search(@NotNull final String baseDN, final int scope,
1520              @NotNull final String filter,
1521              @Nullable final String[] attributes,
1522              final boolean typesOnly,
1523              @Nullable final LDAPSearchConstraints constraints)
1524         throws LDAPException
1525  {
1526    final LDAPSearchResults results;
1527    final LDAPSearchConstraints c =
1528         (constraints == null) ? searchConstraints : constraints;
1529    results = new LDAPSearchResults(c.getTimeLimit());
1530
1531    try
1532    {
1533      final SearchRequest searchRequest = new SearchRequest(results, baseDN,
1534           SearchScope.valueOf(scope), filter, attributes);
1535
1536      searchRequest.setDerefPolicy(
1537           DereferencePolicy.valueOf(c.getDereference()));
1538      searchRequest.setSizeLimit(c.getMaxResults());
1539      searchRequest.setTimeLimitSeconds(c.getServerTimeLimit());
1540      searchRequest.setTypesOnly(typesOnly);
1541
1542      update(searchRequest, constraints);
1543
1544      results.setAsyncRequestID(conn.asyncSearch(searchRequest));
1545      return results;
1546    }
1547    catch (final com.unboundid.ldap.sdk.LDAPException le)
1548    {
1549      Debug.debugException(le);
1550      setResponseControls(le);
1551      throw new LDAPException(le);
1552    }
1553  }
1554
1555
1556
1557  /**
1558   * Retrieves the set of controls to use in a request.
1559   *
1560   * @param  c  The constraints to be applied.
1561   *
1562   * @return  The set of controls to use in a request.
1563   */
1564  @NotNull()
1565  private Control[] getControls(@Nullable final LDAPConstraints c)
1566  {
1567    Control[] controls = null;
1568    if (c != null)
1569    {
1570      controls = LDAPControl.toControls(c.getServerControls());
1571    }
1572    else if (constraints != null)
1573    {
1574      controls = LDAPControl.toControls(constraints.getServerControls());
1575    }
1576
1577    if (controls == null)
1578    {
1579      return new Control[0];
1580    }
1581    else
1582    {
1583      return controls;
1584    }
1585  }
1586
1587
1588
1589  /**
1590   * Updates the provided request to account for the given set of constraints.
1591   *
1592   * @param  request      The request to be updated.
1593   * @param  constraints  The constraints to be applied.
1594   */
1595  private void update(@NotNull final UpdatableLDAPRequest request,
1596                      @Nullable final LDAPConstraints constraints)
1597  {
1598    final LDAPConstraints c =
1599         (constraints == null) ? this.constraints : constraints;
1600
1601    request.setControls(LDAPControl.toControls(c.getServerControls()));
1602    request.setResponseTimeoutMillis(c.getTimeLimit());
1603    request.setFollowReferrals(c.getReferrals());
1604  }
1605
1606
1607
1608  /**
1609   * Sets the response controls for this connection.
1610   *
1611   * @param  ldapResult  The result containing the controls to use.
1612   */
1613  private void setResponseControls(@NotNull final LDAPResult ldapResult)
1614  {
1615    if (ldapResult.hasResponseControl())
1616    {
1617      responseControls =
1618           LDAPControl.toLDAPControls(ldapResult.getResponseControls());
1619    }
1620    else
1621    {
1622      responseControls = null;
1623    }
1624  }
1625
1626
1627
1628  /**
1629   * Sets the response controls for this connection.
1630   *
1631   * @param  ldapException  The exception containing the controls to use.
1632   */
1633  private void setResponseControls(
1634       @NotNull final com.unboundid.ldap.sdk.LDAPException ldapException)
1635  {
1636    if (ldapException.hasResponseControl())
1637    {
1638      responseControls =
1639           LDAPControl.toLDAPControls(ldapException.getResponseControls());
1640    }
1641    else
1642    {
1643      responseControls = null;
1644    }
1645  }
1646}