001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-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) 2007-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;
037
038
039
040import java.util.ArrayList;
041import java.util.Arrays;
042import java.util.List;
043import java.util.concurrent.LinkedBlockingQueue;
044import java.util.concurrent.TimeUnit;
045import java.util.logging.Level;
046
047import com.unboundid.asn1.ASN1Buffer;
048import com.unboundid.asn1.ASN1BufferSequence;
049import com.unboundid.asn1.ASN1Element;
050import com.unboundid.asn1.ASN1Integer;
051import com.unboundid.asn1.ASN1OctetString;
052import com.unboundid.asn1.ASN1Sequence;
053import com.unboundid.ldap.protocol.LDAPMessage;
054import com.unboundid.ldap.protocol.LDAPResponse;
055import com.unboundid.ldap.protocol.ProtocolOp;
056import com.unboundid.util.Debug;
057import com.unboundid.util.InternalUseOnly;
058import com.unboundid.util.LDAPSDKUsageException;
059import com.unboundid.util.NotMutable;
060import com.unboundid.util.NotNull;
061import com.unboundid.util.Nullable;
062import com.unboundid.util.StaticUtils;
063import com.unboundid.util.ThreadSafety;
064import com.unboundid.util.ThreadSafetyLevel;
065
066import static com.unboundid.ldap.sdk.LDAPMessages.*;
067
068
069
070/**
071 * This class implements the processing necessary to perform an LDAPv3 simple
072 * bind operation, which authenticates using a bind DN and password.
073 */
074@NotMutable()
075@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
076public final class SimpleBindRequest
077       extends BindRequest
078       implements ResponseAcceptor, ProtocolOp
079{
080  /**
081   * The BER type to use for the credentials element in a simple bind request
082   * protocol op.
083   */
084  private static final byte CRED_TYPE_SIMPLE = (byte) 0x80;
085
086
087
088  /**
089   * The ASN.1 octet string that will be used for the bind DN if none was
090   * provided.
091   */
092  @NotNull private static final ASN1OctetString NO_BIND_DN =
093       new ASN1OctetString();
094
095
096
097  /**
098   * The ASN.1 octet string that will be used for the bind password if none was
099   * provided.
100   */
101  @NotNull private static final ASN1OctetString NO_PASSWORD =
102       new ASN1OctetString(CRED_TYPE_SIMPLE);
103
104
105
106  /**
107   * The serial version UID for this serializable class.
108   */
109  private static final long serialVersionUID = 4725871243149974407L;
110
111
112
113  // The message ID from the last LDAP message sent from this request.
114  private int messageID = -1;
115
116  // The bind DN for this simple bind request.
117  @NotNull private final ASN1OctetString bindDN;
118
119  // The password for this simple bind request.
120  @Nullable private final ASN1OctetString password;
121
122  // The queue that will be used to receive response messages from the server.
123  @NotNull private final LinkedBlockingQueue<LDAPResponse> responseQueue =
124       new LinkedBlockingQueue<>();
125
126  // The password provider that should be used to obtain the password for this
127  // simple bind request.
128  @Nullable private final PasswordProvider passwordProvider;
129
130
131
132  /**
133   * Creates a new simple bind request that may be used to perform an anonymous
134   * bind to the directory server (i.e., with a zero-length bind DN and a
135   * zero-length password).
136   */
137  public SimpleBindRequest()
138  {
139    this(NO_BIND_DN, NO_PASSWORD, null, NO_CONTROLS);
140  }
141
142
143
144  /**
145   * Creates a new simple bind request with the provided bind DN and password.
146   *
147   * @param  bindDN    The bind DN for this simple bind request.
148   * @param  password  The password for this simple bind request.
149   */
150  public SimpleBindRequest(@Nullable final String bindDN,
151                           @Nullable final String password)
152  {
153    this(bindDN, password, NO_CONTROLS);
154  }
155
156
157
158  /**
159   * Creates a new simple bind request with the provided bind DN and password.
160   *
161   * @param  bindDN    The bind DN for this simple bind request.
162   * @param  password  The password for this simple bind request.
163   */
164  public SimpleBindRequest(@Nullable final String bindDN,
165                           @Nullable final byte[] password)
166  {
167    this(bindDN, password, NO_CONTROLS);
168  }
169
170
171
172  /**
173   * Creates a new simple bind request with the provided bind DN and password.
174   *
175   * @param  bindDN    The bind DN for this simple bind request.
176   * @param  password  The password for this simple bind request.
177   */
178  public SimpleBindRequest(@Nullable final DN bindDN,
179                           @Nullable final String password)
180  {
181    this(bindDN, password, NO_CONTROLS);
182  }
183
184
185
186  /**
187   * Creates a new simple bind request with the provided bind DN and password.
188   *
189   * @param  bindDN    The bind DN for this simple bind request.
190   * @param  password  The password for this simple bind request.
191   */
192  public SimpleBindRequest(@Nullable final DN bindDN,
193                           @Nullable final byte[] password)
194  {
195    this(bindDN, password, NO_CONTROLS);
196  }
197
198
199
200  /**
201   * Creates a new simple bind request with the provided bind DN and password.
202   *
203   * @param  bindDN    The bind DN for this simple bind request.
204   * @param  password  The password for this simple bind request.
205   * @param  controls  The set of controls for this simple bind request.
206   */
207  public SimpleBindRequest(@Nullable final String bindDN,
208                           @Nullable final String password,
209                           @Nullable final Control... controls)
210  {
211    super(controls);
212
213    if (bindDN == null)
214    {
215      this.bindDN = NO_BIND_DN;
216    }
217    else
218    {
219      this.bindDN = new ASN1OctetString(bindDN);
220    }
221
222    if (password == null)
223    {
224      this.password = NO_PASSWORD;
225    }
226    else
227    {
228      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
229    }
230
231    passwordProvider = null;
232  }
233
234
235
236  /**
237   * Creates a new simple bind request with the provided bind DN and password.
238   *
239   * @param  bindDN    The bind DN for this simple bind request.
240   * @param  password  The password for this simple bind request.
241   * @param  controls  The set of controls for this simple bind request.
242   */
243  public SimpleBindRequest(@Nullable final String bindDN,
244                           @Nullable final byte[] password,
245                           @Nullable final Control... controls)
246  {
247    super(controls);
248
249    if (bindDN == null)
250    {
251      this.bindDN = NO_BIND_DN;
252    }
253    else
254    {
255      this.bindDN = new ASN1OctetString(bindDN);
256    }
257
258    if (password == null)
259    {
260      this.password = NO_PASSWORD;
261    }
262    else
263    {
264      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
265    }
266
267    passwordProvider = null;
268  }
269
270
271
272  /**
273   * Creates a new simple bind request with the provided bind DN and password.
274   *
275   * @param  bindDN    The bind DN for this simple bind request.
276   * @param  password  The password for this simple bind request.
277   * @param  controls  The set of controls for this simple bind request.
278   */
279  public SimpleBindRequest(@Nullable final DN bindDN,
280                           @Nullable final String password,
281                           @Nullable final Control... controls)
282  {
283    super(controls);
284
285    if (bindDN == null)
286    {
287      this.bindDN = NO_BIND_DN;
288    }
289    else
290    {
291      this.bindDN = new ASN1OctetString(bindDN.toString());
292    }
293
294    if (password == null)
295    {
296      this.password = NO_PASSWORD;
297    }
298    else
299    {
300      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
301    }
302
303    passwordProvider = null;
304  }
305
306
307
308  /**
309   * Creates a new simple bind request with the provided bind DN and password.
310   *
311   * @param  bindDN    The bind DN for this simple bind request.
312   * @param  password  The password for this simple bind request.
313   * @param  controls  The set of controls for this simple bind request.
314   */
315  public SimpleBindRequest(@Nullable final DN bindDN,
316                           @Nullable final byte[] password,
317                           @Nullable final Control... controls)
318  {
319    super(controls);
320
321    if (bindDN == null)
322    {
323      this.bindDN = NO_BIND_DN;
324    }
325    else
326    {
327      this.bindDN = new ASN1OctetString(bindDN.toString());
328    }
329
330    if (password == null)
331    {
332      this.password = NO_PASSWORD;
333    }
334    else
335    {
336      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
337    }
338
339    passwordProvider = null;
340  }
341
342
343
344  /**
345   * Creates a new simple bind request with the provided bind DN and that will
346   * use a password provider in order to obtain the bind password.
347   *
348   * @param  bindDN            The bind DN for this simple bind request.  It
349   *                           must not be {@code null}.
350   * @param  passwordProvider  The password provider that will be used to obtain
351   *                           the password for this simple bind request.  It
352   *                           must not be {@code null}.
353   * @param  controls          The set of controls for this simple bind request.
354   */
355  public SimpleBindRequest(@NotNull final String bindDN,
356                           @NotNull final PasswordProvider passwordProvider,
357                           @Nullable final Control... controls)
358  {
359    super(controls);
360
361    this.bindDN           = new ASN1OctetString(bindDN);
362    this.passwordProvider = passwordProvider;
363
364    password = null;
365  }
366
367
368
369  /**
370   * Creates a new simple bind request with the provided bind DN and that will
371   * use a password provider in order to obtain the bind password.
372   *
373   * @param  bindDN            The bind DN for this simple bind request.  It
374   *                           must not be {@code null}.
375   * @param  passwordProvider  The password provider that will be used to obtain
376   *                           the password for this simple bind request.  It
377   *                           must not be {@code null}.
378   * @param  controls          The set of controls for this simple bind request.
379   */
380  public SimpleBindRequest(@NotNull final DN bindDN,
381                           @NotNull final PasswordProvider passwordProvider,
382                           @Nullable final Control... controls)
383  {
384    super(controls);
385
386    this.bindDN           = new ASN1OctetString(bindDN.toString());
387    this.passwordProvider = passwordProvider;
388
389    password = null;
390  }
391
392
393
394  /**
395   * Creates a new simple bind request with the provided bind DN and password.
396   *
397   * @param  bindDN            The bind DN for this simple bind request.
398   * @param  password          The password for this simple bind request.
399   * @param  passwordProvider  The password provider that will be used to obtain
400   *                           the password to use for the bind request.
401   * @param  controls          The set of controls for this simple bind request.
402   */
403  private SimpleBindRequest(@Nullable final ASN1OctetString bindDN,
404                            @Nullable final ASN1OctetString password,
405                            @Nullable final PasswordProvider passwordProvider,
406                            @Nullable final Control... controls)
407  {
408    super(controls);
409
410    this.bindDN           = bindDN;
411    this.password         = password;
412    this.passwordProvider = passwordProvider;
413  }
414
415
416
417  /**
418   * Retrieves the bind DN for this simple bind request.
419   *
420   * @return  The bind DN for this simple bind request.
421   */
422  @NotNull()
423  public String getBindDN()
424  {
425    return bindDN.stringValue();
426  }
427
428
429
430  /**
431   * Retrieves the password for this simple bind request, if no password
432   * provider has been configured.
433   *
434   * @return  The password for this simple bind request, or {@code null} if a
435   *          password provider will be used to obtain the password.
436   */
437  @Nullable()
438  public ASN1OctetString getPassword()
439  {
440    return password;
441  }
442
443
444
445  /**
446   * Retrieves the password provider for this simple bind request, if defined.
447   *
448   * @return  The password provider for this simple bind request, or
449   *          {@code null} if this bind request was created with an explicit
450   *          password rather than a password provider.
451   */
452  @Nullable()
453  public PasswordProvider getPasswordProvider()
454  {
455    return passwordProvider;
456  }
457
458
459
460  /**
461   * {@inheritDoc}
462   */
463  @Override()
464  public byte getProtocolOpType()
465  {
466    return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
467  }
468
469
470
471  /**
472   * {@inheritDoc}
473   */
474  @Override()
475  public void writeTo(@NotNull final ASN1Buffer buffer)
476  {
477    final ASN1BufferSequence requestSequence =
478         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
479    buffer.addElement(VERSION_ELEMENT);
480    buffer.addElement(bindDN);
481
482    if (passwordProvider == null)
483    {
484      buffer.addElement(password);
485    }
486    else
487    {
488      final byte[] pwBytes;
489      try
490      {
491        pwBytes = passwordProvider.getPasswordBytes();
492      }
493      catch (final LDAPException le)
494      {
495        Debug.debugException(le);
496        throw new LDAPRuntimeException(le);
497      }
498
499      final ASN1OctetString pw = new ASN1OctetString(CRED_TYPE_SIMPLE, pwBytes);
500      buffer.addElement(pw);
501      buffer.setZeroBufferOnClear();
502      Arrays.fill(pwBytes, (byte) 0x00);
503    }
504
505    requestSequence.end();
506  }
507
508
509
510  /**
511   * {@inheritDoc}
512   * Use of this method is only supported if the bind request was created with a
513   * static password.  It is not allowed if the password will be obtained
514   * through a password provider.
515   *
516   * @throws  LDAPSDKUsageException  If this bind request was created with a
517   *                                 password provider rather than a static
518   *                                 password.
519   */
520  @Override()
521  @NotNull()
522  public ASN1Element encodeProtocolOp()
523         throws LDAPSDKUsageException
524  {
525    if (password == null)
526    {
527      throw new LDAPSDKUsageException(
528           ERR_SIMPLE_BIND_ENCODE_PROTOCOL_OP_WITH_PROVIDER.get());
529    }
530
531    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
532         new ASN1Integer(3),
533         bindDN,
534         password);
535  }
536
537
538
539  /**
540   * {@inheritDoc}
541   */
542  @Override()
543  @NotNull()
544  protected BindResult process(@NotNull final LDAPConnection connection,
545                               final int depth)
546            throws LDAPException
547  {
548    setReferralDepth(depth);
549
550    // See if a bind DN was provided without a password.  If that is the case
551    // and this should not be allowed, then throw an exception.
552    if (password != null)
553    {
554      if ((bindDN.getValue().length > 0) && (password.getValue().length == 0) &&
555           connection.getConnectionOptions().bindWithDNRequiresPassword())
556      {
557        final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
558             ERR_SIMPLE_BIND_DN_WITHOUT_PASSWORD.get());
559        Debug.debugCodingError(le);
560        throw le;
561      }
562    }
563
564
565    if (connection.synchronousMode())
566    {
567      @SuppressWarnings("deprecation")
568      final boolean autoReconnect =
569           connection.getConnectionOptions().autoReconnect();
570      return processSync(connection, autoReconnect);
571    }
572
573    // Create the LDAP message.
574    messageID = connection.nextMessageID();
575    final LDAPMessage message = new LDAPMessage(messageID, this, getControls());
576
577
578    // Register with the connection reader to be notified of responses for the
579    // request that we've created.
580    connection.registerResponseAcceptor(messageID, this);
581
582
583    try
584    {
585      // Send the request to the server.
586      final long responseTimeout = getResponseTimeoutMillis(connection);
587      Debug.debugLDAPRequest(Level.INFO, this, messageID, connection);
588
589      final LDAPConnectionLogger logger =
590           connection.getConnectionOptions().getConnectionLogger();
591      if (logger != null)
592      {
593        logger.logBindRequest(connection, messageID, this);
594      }
595
596      final long requestTime = System.nanoTime();
597      connection.getConnectionStatistics().incrementNumBindRequests();
598      connection.sendMessage(message, responseTimeout);
599
600      // Wait for and process the response.
601      final LDAPResponse response;
602      try
603      {
604        if (responseTimeout > 0)
605        {
606          response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
607        }
608        else
609        {
610          response = responseQueue.take();
611        }
612      }
613      catch (final InterruptedException ie)
614      {
615        Debug.debugException(ie);
616        Thread.currentThread().interrupt();
617        throw new LDAPException(ResultCode.LOCAL_ERROR,
618             ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie);
619      }
620
621      return handleResponse(connection, response, requestTime, false);
622    }
623    finally
624    {
625      connection.deregisterResponseAcceptor(messageID);
626    }
627  }
628
629
630
631  /**
632   * Processes this bind operation in synchronous mode, in which the same
633   * thread will send the request and read the response.
634   *
635   * @param  connection  The connection to use to communicate with the directory
636   *                     server.
637   * @param  allowRetry  Indicates whether the request may be re-tried on a
638   *                     re-established connection if the initial attempt fails
639   *                     in a way that indicates the connection is no longer
640   *                     valid and autoReconnect is true.
641   *
642   * @return  An LDAP result object that provides information about the result
643   *          of the bind processing.
644   *
645   * @throws  LDAPException  If a problem occurs while sending the request or
646   *                         reading the response.
647   */
648  @NotNull()
649  private BindResult processSync(@NotNull final LDAPConnection connection,
650                                 final boolean allowRetry)
651          throws LDAPException
652  {
653    // Create the LDAP message.
654    messageID = connection.nextMessageID();
655    final LDAPMessage message =
656         new LDAPMessage(messageID, this, getControls());
657
658
659    // Send the request to the server.
660    final long requestTime = System.nanoTime();
661    Debug.debugLDAPRequest(Level.INFO, this, messageID, connection);
662
663    final LDAPConnectionLogger logger =
664         connection.getConnectionOptions().getConnectionLogger();
665    if (logger != null)
666    {
667      logger.logBindRequest(connection, messageID, this);
668    }
669
670    connection.getConnectionStatistics().incrementNumBindRequests();
671    try
672    {
673      connection.sendMessage(message, getResponseTimeoutMillis(connection));
674    }
675    catch (final LDAPException le)
676    {
677      Debug.debugException(le);
678
679      if (allowRetry)
680      {
681        final BindResult bindResult = reconnectAndRetry(connection,
682             le.getResultCode());
683        if (bindResult != null)
684        {
685          return bindResult;
686        }
687      }
688
689      throw le;
690    }
691
692    while (true)
693    {
694      final LDAPResponse response = connection.readResponse(messageID);
695      if (response instanceof IntermediateResponse)
696      {
697        final IntermediateResponseListener listener =
698             getIntermediateResponseListener();
699        if (listener != null)
700        {
701          listener.intermediateResponseReturned(
702               (IntermediateResponse) response);
703        }
704      }
705      else
706      {
707        return handleResponse(connection, response, requestTime, allowRetry);
708      }
709    }
710  }
711
712
713
714  /**
715   * Performs the necessary processing for handling a response.
716   *
717   * @param  connection   The connection used to read the response.
718   * @param  response     The response to be processed.
719   * @param  requestTime  The time the request was sent to the server.
720   * @param  allowRetry   Indicates whether the request may be re-tried on a
721   *                      re-established connection if the initial attempt fails
722   *                      in a way that indicates the connection is no longer
723   *                      valid and autoReconnect is true.
724   *
725   * @return  The bind result.
726   *
727   * @throws  LDAPException  If a problem occurs.
728   */
729  @NotNull()
730  private BindResult handleResponse(@NotNull final LDAPConnection connection,
731                                    @Nullable final LDAPResponse response,
732                                    final long requestTime,
733                                    final boolean allowRetry)
734          throws LDAPException
735  {
736    if (response == null)
737    {
738      final long waitTime =
739           StaticUtils.nanosToMillis(System.nanoTime() - requestTime);
740      throw new LDAPException(ResultCode.TIMEOUT,
741           ERR_SIMPLE_BIND_CLIENT_TIMEOUT.get(waitTime, messageID,
742                bindDN.stringValue(), connection.getHostPort()));
743    }
744
745    connection.getConnectionStatistics().incrementNumBindResponses(
746         System.nanoTime() - requestTime);
747    if (response instanceof ConnectionClosedResponse)
748    {
749      // The connection was closed while waiting for the response.
750      if (allowRetry)
751      {
752        final BindResult retryResult = reconnectAndRetry(connection,
753             ResultCode.SERVER_DOWN);
754        if (retryResult != null)
755        {
756          return retryResult;
757        }
758      }
759
760      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
761      final String message = ccr.getMessage();
762      if (message == null)
763      {
764        throw new LDAPException(ccr.getResultCode(),
765             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
766                  connection.getHostPort(), toString()));
767      }
768      else
769      {
770        throw new LDAPException(ccr.getResultCode(),
771             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
772                  connection.getHostPort(), toString(), message));
773      }
774    }
775
776    final BindResult bindResult = (BindResult) response;
777    if (allowRetry)
778    {
779      final BindResult retryResult = reconnectAndRetry(connection,
780           bindResult.getResultCode());
781      if (retryResult != null)
782      {
783        return retryResult;
784      }
785    }
786
787    return bindResult;
788  }
789
790
791
792  /**
793   * Attempts to re-establish the connection and retry processing this request
794   * on it.
795   *
796   * @param  connection  The connection to be re-established.
797   * @param  resultCode  The result code for the previous operation attempt.
798   *
799   * @return  The result from re-trying the bind, or {@code null} if it could
800   *          not be re-tried.
801   */
802  @Nullable()
803  private BindResult reconnectAndRetry(@NotNull final LDAPConnection connection,
804                                       @NotNull final ResultCode resultCode)
805  {
806    try
807    {
808      // We will only want to retry for certain result codes that indicate a
809      // connection problem.
810      switch (resultCode.intValue())
811      {
812        case ResultCode.SERVER_DOWN_INT_VALUE:
813        case ResultCode.DECODING_ERROR_INT_VALUE:
814        case ResultCode.CONNECT_ERROR_INT_VALUE:
815          connection.reconnect();
816          return processSync(connection, false);
817      }
818    }
819    catch (final Exception e)
820    {
821      Debug.debugException(e);
822    }
823
824    return null;
825  }
826
827
828
829  /**
830   * {@inheritDoc}
831   */
832  @Override()
833  @NotNull()
834  public SimpleBindRequest getRebindRequest(@NotNull final String host,
835                                            final int port)
836  {
837    return new SimpleBindRequest(bindDN, password, passwordProvider,
838         getControls());
839  }
840
841
842
843  /**
844   * {@inheritDoc}
845   */
846  @InternalUseOnly()
847  @Override()
848  public void responseReceived(@NotNull final LDAPResponse response)
849         throws LDAPException
850  {
851    try
852    {
853      responseQueue.put(response);
854    }
855    catch (final Exception e)
856    {
857      Debug.debugException(e);
858
859      if (e instanceof InterruptedException)
860      {
861        Thread.currentThread().interrupt();
862      }
863
864      throw new LDAPException(ResultCode.LOCAL_ERROR,
865           ERR_EXCEPTION_HANDLING_RESPONSE.get(
866                StaticUtils.getExceptionMessage(e)),
867           e);
868    }
869  }
870
871
872
873  /**
874   * {@inheritDoc}
875   */
876  @Override()
877  @NotNull()
878  public String getBindType()
879  {
880    return "SIMPLE";
881  }
882
883
884
885  /**
886   * {@inheritDoc}
887   */
888  @Override()
889  public int getLastMessageID()
890  {
891    return messageID;
892  }
893
894
895
896  /**
897   * {@inheritDoc}
898   */
899  @Override()
900  @NotNull()
901  public SimpleBindRequest duplicate()
902  {
903    return duplicate(getControls());
904  }
905
906
907
908  /**
909   * {@inheritDoc}
910   */
911  @Override()
912  @NotNull()
913  public SimpleBindRequest duplicate(@Nullable final Control[] controls)
914  {
915    final SimpleBindRequest bindRequest =
916         new SimpleBindRequest(bindDN, password, passwordProvider, controls);
917    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
918    bindRequest.setIntermediateResponseListener(
919         getIntermediateResponseListener());
920    bindRequest.setReferralDepth(getReferralDepth());
921    bindRequest.setReferralConnector(getReferralConnectorInternal());
922    return bindRequest;
923  }
924
925
926
927  /**
928   * {@inheritDoc}
929   */
930  @Override()
931  public void toString(@NotNull final StringBuilder buffer)
932  {
933    buffer.append("SimpleBindRequest(dn='");
934    buffer.append(bindDN);
935    buffer.append('\'');
936
937    final Control[] controls = getControls();
938    if (controls.length > 0)
939    {
940      buffer.append(", controls={");
941      for (int i=0; i < controls.length; i++)
942      {
943        if (i > 0)
944        {
945          buffer.append(", ");
946        }
947
948        buffer.append(controls[i]);
949      }
950      buffer.append('}');
951    }
952
953    buffer.append(')');
954  }
955
956
957
958  /**
959   * {@inheritDoc}
960   */
961  @Override()
962  public void toCode(@NotNull final List<String> lineList,
963                     @NotNull final String requestID,
964                     final int indentSpaces, final boolean includeProcessing)
965  {
966    // Create the request variable.
967    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(3);
968    constructorArgs.add(ToCodeArgHelper.createString(bindDN.stringValue(),
969         "Bind DN"));
970    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
971         "Bind Password"));
972
973    final Control[] controls = getControls();
974    if (controls.length > 0)
975    {
976      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
977           "Bind Controls"));
978    }
979
980    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "SimpleBindRequest",
981         requestID + "Request", "new SimpleBindRequest", constructorArgs);
982
983
984    // Add lines for processing the request and obtaining the result.
985    if (includeProcessing)
986    {
987      // Generate a string with the appropriate indent.
988      final StringBuilder buffer = new StringBuilder();
989      for (int i=0; i < indentSpaces; i++)
990      {
991        buffer.append(' ');
992      }
993      final String indent = buffer.toString();
994
995      lineList.add("");
996      lineList.add(indent + "try");
997      lineList.add(indent + '{');
998      lineList.add(indent + "  BindResult " + requestID +
999           "Result = connection.bind(" + requestID + "Request);");
1000      lineList.add(indent + "  // The bind was processed successfully.");
1001      lineList.add(indent + '}');
1002      lineList.add(indent + "catch (LDAPException e)");
1003      lineList.add(indent + '{');
1004      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
1005           "help explain why.");
1006      lineList.add(indent + "  // Note that the connection is now likely in " +
1007           "an unauthenticated state.");
1008      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
1009      lineList.add(indent + "  String message = e.getMessage();");
1010      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
1011      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
1012      lineList.add(indent + "  Control[] responseControls = " +
1013           "e.getResponseControls();");
1014      lineList.add(indent + '}');
1015    }
1016  }
1017}