001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2016 ForgeRock AS.
016 */
017package org.opends.server.tools;
018
019import static com.forgerock.opendj.cli.ArgumentConstants.*;
020import static com.forgerock.opendj.cli.CliMessages.INFO_DESCRIPTION_BINDPASSWORDFILE;
021import static com.forgerock.opendj.cli.Utils.*;
022import static com.forgerock.opendj.cli.CommonArguments.*;
023
024import static org.opends.messages.ToolMessages.*;
025import static org.opends.server.protocols.ldap.LDAPResultCode.*;
026import static org.opends.server.util.cli.LDAPConnectionArgumentParser.*;
027
028import java.io.BufferedReader;
029import java.io.FileReader;
030import java.io.IOException;
031import java.io.InputStreamReader;
032import java.io.OutputStream;
033import java.io.PrintStream;
034import java.io.Reader;
035import java.util.ArrayList;
036import java.util.List;
037import java.util.concurrent.atomic.AtomicInteger;
038
039import org.forgerock.i18n.LocalizableMessage;
040import org.forgerock.i18n.slf4j.LocalizedLogger;
041import org.forgerock.opendj.ldap.ByteString;
042import org.forgerock.opendj.ldap.DecodeException;
043import org.opends.server.controls.SubtreeDeleteControl;
044import org.opends.server.core.DirectoryServer.DirectoryServerVersionHandler;
045import org.opends.server.protocols.ldap.DeleteRequestProtocolOp;
046import org.opends.server.protocols.ldap.DeleteResponseProtocolOp;
047import org.opends.server.protocols.ldap.LDAPMessage;
048import org.opends.server.protocols.ldap.ProtocolOp;
049import org.opends.server.types.Control;
050import org.opends.server.types.LDAPException;
051import org.opends.server.types.NullOutputStream;
052import org.opends.server.util.EmbeddedUtils;
053
054import com.forgerock.opendj.cli.ArgumentException;
055import com.forgerock.opendj.cli.ArgumentParser;
056import com.forgerock.opendj.cli.BooleanArgument;
057import com.forgerock.opendj.cli.CliConstants;
058import com.forgerock.opendj.cli.FileBasedArgument;
059import com.forgerock.opendj.cli.IntegerArgument;
060import com.forgerock.opendj.cli.StringArgument;
061
062/**
063 * This class provides a tool that can be used to issue delete requests to the
064 * Directory Server.
065 */
066public class LDAPDelete
067{
068  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
069
070  /** The fully-qualified name of this class. */
071  private static final String CLASS_NAME = "org.opends.server.tools.LDAPDelete";
072
073
074  /** The message ID counter to use for requests. */
075  private final AtomicInteger nextMessageID;
076
077  /** The print stream to use for standard error. */
078  private final PrintStream err;
079  /** The print stream to use for standard output. */
080  private final PrintStream out;
081
082
083
084  /**
085   * Constructor for the LDAPDelete object.
086   *
087   * @param  nextMessageID  The next message ID to use for requests.
088   * @param  out            The print stream to use for standard output.
089   * @param  err            The print stream to use for standard error.
090   */
091  public LDAPDelete(AtomicInteger nextMessageID, PrintStream out,
092                    PrintStream err)
093  {
094    this.nextMessageID = nextMessageID;
095    this.out           = out;
096    this.err           = err;
097  }
098
099  /**
100   * Execute the delete request on the specified list of DNs.
101   *
102   * @param connection        The connection to use to execute the request.
103   * @param lines             The list of DNs to delete.
104   * @param deleteOptions     The constraints to use for this request.
105   *
106   * @throws  IOException  If a problem occurs while attempting to communicate
107   *                       with the Directory Server.
108   *
109   * @throws  LDAPException  If the Directory Server returns an error response.
110   */
111  public void readAndExecute(LDAPConnection connection,
112                             List<String> lines,
113                             LDAPDeleteOptions deleteOptions)
114    throws IOException, LDAPException
115  {
116    for(String line : lines)
117    {
118      executeDelete(connection, line, deleteOptions);
119    }
120  }
121
122  /**
123   * Read the specified DNs from the given reader
124   * (file or stdin) and execute the given delete request.
125   *
126   * @param connection        The connection to use to execute the request.
127   * @param reader            The reader to read the list of DNs from.
128   * @param deleteOptions     The constraints to use for this request.
129   *
130   * @throws  IOException  If a problem occurs while attempting to communicate
131   *                       with the Directory Server.
132   *
133   * @throws  LDAPException  If the Directory Server returns an error response.
134   */
135  public void readAndExecute(LDAPConnection connection, Reader reader,
136                             LDAPDeleteOptions deleteOptions)
137         throws IOException, LDAPException
138  {
139    BufferedReader in = new BufferedReader(reader);
140    String line = null;
141
142    while ((line = in.readLine()) != null)
143    {
144      executeDelete(connection, line, deleteOptions);
145    }
146    in.close();
147  }
148
149
150  /**
151   * Execute the delete request for the specified DN.
152   *
153   * @param connection        The connection to use to execute the request.
154   * @param line           The DN to delete.
155   * @param deleteOptions  The list of constraints for this request.
156   *
157   * @throws  IOException  If a problem occurs while attempting to communicate
158   *                       with the Directory Server.
159   *
160   * @throws  LDAPException  If the Directory Server returns an error response.
161   */
162  private void executeDelete(LDAPConnection connection, String line,
163                             LDAPDeleteOptions deleteOptions)
164          throws IOException, LDAPException
165  {
166    ArrayList<Control> controls = deleteOptions.getControls();
167    ProtocolOp protocolOp = null;
168    ByteString asn1OctetStr = ByteString.valueOfUtf8(line);
169
170    protocolOp = new DeleteRequestProtocolOp(asn1OctetStr);
171
172    out.println(INFO_PROCESSING_OPERATION.get("DELETE", asn1OctetStr));
173    if(!deleteOptions.showOperations())
174    {
175      LDAPMessage message = new LDAPMessage(nextMessageID.getAndIncrement(),
176                                            protocolOp, controls);
177      LDAPMessage responseMessage = null;
178      try
179      {
180        connection.getLDAPWriter().writeMessage(message);
181        responseMessage = connection.getLDAPReader().readMessage();
182      } catch(DecodeException ae)
183      {
184        logger.traceException(ae);
185        if (!deleteOptions.continueOnError())
186        {
187          String msg = LDAPToolUtils.getMessageForConnectionException(ae);
188          throw new IOException(msg, ae);
189        }
190        else
191        {
192          printWrappedText(err, INFO_OPERATION_FAILED.get("DELETE"));
193          printWrappedText(err, ae.getMessage());
194          return;
195        }
196      }
197
198      DeleteResponseProtocolOp op =
199           responseMessage.getDeleteResponseProtocolOp();
200      int resultCode = op.getResultCode();
201      LocalizableMessage errorMessage = op.getErrorMessage();
202      if(resultCode != SUCCESS && resultCode != REFERRAL &&
203         !deleteOptions.continueOnError())
204      {
205        LocalizableMessage msg = INFO_OPERATION_FAILED.get("DELETE");
206        throw new LDAPException(resultCode, errorMessage, msg,
207                                op.getMatchedDN(), null);
208      } else
209      {
210        if(resultCode != SUCCESS && resultCode != REFERRAL)
211        {
212          LocalizableMessage msg = INFO_OPERATION_FAILED.get("DELETE");
213          LDAPToolUtils.printErrorMessage(err, msg, resultCode, errorMessage,
214                                          op.getMatchedDN());
215        } else
216        {
217          LocalizableMessage msg = INFO_OPERATION_SUCCESSFUL.get("DELETE", line);
218          out.println(msg);
219        }
220      }
221    }
222  }
223
224  /**
225   * The main method for LDAPDelete tool.
226   *
227   * @param  args  The command-line arguments provided to this program.
228   */
229
230  public static void main(String[] args)
231  {
232    int retCode = mainDelete(args, true, System.out, System.err);
233
234    if(retCode != 0)
235    {
236      System.exit(filterExitCode(retCode));
237    }
238  }
239
240  /**
241   * Parses the provided command-line arguments and uses that information to
242   * run the ldapdelete tool.
243   *
244   * @param  args  The command-line arguments provided to this program.
245   *
246   * @return The error code.
247   */
248
249  public static int mainDelete(String[] args)
250  {
251    return mainDelete(args, true, System.out, System.err);
252  }
253
254  /**
255   * Parses the provided command-line arguments and uses that information to
256   * run the ldapdelete tool.
257   *
258   * @param  args              The command-line arguments provided to this
259   *                           program.
260   * @param  initializeServer  Indicates whether to initialize the server.
261   * @param  outStream         The output stream to use for standard output, or
262   *                           <CODE>null</CODE> if standard output is not
263   *                           needed.
264   * @param  errStream         The output stream to use for standard error, or
265   *                           <CODE>null</CODE> if standard error is not
266   *                           needed.
267   *
268   * @return The error code.
269   */
270
271  public static int mainDelete(String[] args, boolean initializeServer,
272                               OutputStream outStream, OutputStream errStream)
273  {
274    PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
275    PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
276
277    LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
278    LDAPDeleteOptions deleteOptions = new LDAPDeleteOptions();
279    LDAPConnection connection = null;
280
281    final BooleanArgument continueOnError;
282    final BooleanArgument deleteSubtree;
283    final BooleanArgument noop;
284    final BooleanArgument saslExternal;
285    final BooleanArgument showUsage;
286    final BooleanArgument startTLS;
287    final BooleanArgument trustAll;
288    final BooleanArgument useSSL;
289    final BooleanArgument verbose;
290    final FileBasedArgument bindPasswordFile;
291    final FileBasedArgument keyStorePasswordFile;
292    final FileBasedArgument trustStorePasswordFile;
293    final IntegerArgument port;
294    final IntegerArgument version;
295    final StringArgument bindDN;
296    final StringArgument bindPassword;
297    final StringArgument certNickname;
298    final StringArgument controlStr;
299    final StringArgument encodingStr;
300    final StringArgument filename;
301    final StringArgument hostName;
302    final StringArgument keyStorePath;
303    final StringArgument keyStorePassword;
304    final StringArgument saslOptions;
305    final StringArgument trustStorePath;
306    final StringArgument trustStorePassword;
307    final IntegerArgument connectTimeout;
308    final StringArgument propertiesFileArgument;
309    final BooleanArgument noPropertiesFileArgument;
310
311    Reader rdr = null;
312    List<String> dnStrings = new ArrayList<>();
313
314    // Create the command-line argument parser for use with this program.
315    LocalizableMessage toolDescription = INFO_LDAPDELETE_TOOL_DESCRIPTION.get();
316    ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
317                                                  false, true, 0, 1, "\"DN\"");
318    argParser.setShortToolDescription(REF_SHORT_DESC_LDAPDELETE.get());
319    argParser.setVersionHandler(new DirectoryServerVersionHandler());
320    try
321    {
322      propertiesFileArgument =
323              StringArgument.builder(OPTION_LONG_PROP_FILE_PATH)
324                      .description(INFO_DESCRIPTION_PROP_FILE_PATH.get())
325                      .valuePlaceholder(INFO_PROP_FILE_PATH_PLACEHOLDER.get())
326                      .buildAndAddToParser(argParser);
327      argParser.setFilePropertiesArgument(propertiesFileArgument);
328
329      noPropertiesFileArgument =
330              BooleanArgument.builder(OPTION_LONG_NO_PROP_FILE)
331                      .description(INFO_DESCRIPTION_NO_PROP_FILE.get())
332                      .buildAndAddToParser(argParser);
333      argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
334
335      hostName =
336              StringArgument.builder(OPTION_LONG_HOST)
337                      .shortIdentifier(OPTION_SHORT_HOST)
338                      .description(INFO_DESCRIPTION_HOST.get())
339                      .defaultValue("localhost")
340                      .valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
341                      .buildAndAddToParser(argParser);
342      port =
343              IntegerArgument.builder(OPTION_LONG_PORT)
344                      .shortIdentifier(OPTION_SHORT_PORT)
345                      .description(INFO_DESCRIPTION_PORT.get())
346                      .range(1, 65535)
347                      .defaultValue(389)
348                      .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
349                      .buildAndAddToParser(argParser);
350      useSSL =
351              BooleanArgument.builder(OPTION_LONG_USE_SSL)
352                      .shortIdentifier(OPTION_SHORT_USE_SSL)
353                      .description(INFO_DESCRIPTION_USE_SSL.get())
354                      .buildAndAddToParser(argParser);
355      startTLS =
356              BooleanArgument.builder(OPTION_LONG_START_TLS)
357                      .shortIdentifier(OPTION_SHORT_START_TLS)
358                      .description(INFO_DESCRIPTION_START_TLS.get())
359                      .buildAndAddToParser(argParser);
360      bindDN =
361              StringArgument.builder(OPTION_LONG_BINDDN)
362                      .shortIdentifier(OPTION_SHORT_BINDDN)
363                      .description(INFO_DESCRIPTION_BINDDN.get())
364                      .valuePlaceholder(INFO_BINDDN_PLACEHOLDER.get())
365                      .buildAndAddToParser(argParser);
366      bindPassword =
367              StringArgument.builder(OPTION_LONG_BINDPWD)
368                      .shortIdentifier(OPTION_SHORT_BINDPWD)
369                      .description(INFO_DESCRIPTION_BINDPASSWORD.get())
370                      .valuePlaceholder(INFO_BINDPWD_PLACEHOLDER.get())
371                      .buildAndAddToParser(argParser);
372      bindPasswordFile =
373              FileBasedArgument.builder(OPTION_LONG_BINDPWD_FILE)
374                      .shortIdentifier(OPTION_SHORT_BINDPWD_FILE)
375                      .description(INFO_DESCRIPTION_BINDPASSWORDFILE.get())
376                      .valuePlaceholder(INFO_BINDPWD_FILE_PLACEHOLDER.get())
377                      .buildAndAddToParser(argParser);
378      filename =
379              StringArgument.builder(OPTION_LONG_FILENAME)
380                      .shortIdentifier(OPTION_SHORT_FILENAME)
381                      .description(INFO_DELETE_DESCRIPTION_FILENAME.get())
382                      .valuePlaceholder(INFO_FILE_PLACEHOLDER.get())
383                      .buildAndAddToParser(argParser);
384      saslExternal =
385              BooleanArgument.builder("useSASLExternal")
386                      .shortIdentifier('r')
387                      .description(INFO_DESCRIPTION_USE_SASL_EXTERNAL.get())
388                      .buildAndAddToParser(argParser);
389      saslOptions =
390              StringArgument.builder(OPTION_LONG_SASLOPTION)
391                      .shortIdentifier(OPTION_SHORT_SASLOPTION)
392                      .description(INFO_DESCRIPTION_SASL_PROPERTIES.get())
393                      .multiValued()
394                      .valuePlaceholder(INFO_SASL_OPTION_PLACEHOLDER.get())
395                      .buildAndAddToParser(argParser);
396
397      trustAll = trustAllArgument();
398      argParser.addArgument(trustAll);
399
400      keyStorePath =
401              StringArgument.builder(OPTION_LONG_KEYSTOREPATH)
402                      .shortIdentifier(OPTION_SHORT_KEYSTOREPATH)
403                      .description(INFO_DESCRIPTION_KEYSTOREPATH.get())
404                      .valuePlaceholder(INFO_KEYSTOREPATH_PLACEHOLDER.get())
405                      .buildAndAddToParser(argParser);
406      keyStorePassword =
407              StringArgument.builder(OPTION_LONG_KEYSTORE_PWD)
408                      .shortIdentifier(OPTION_SHORT_KEYSTORE_PWD)
409                      .description(INFO_DESCRIPTION_KEYSTOREPASSWORD.get())
410                      .valuePlaceholder(INFO_KEYSTORE_PWD_PLACEHOLDER.get())
411                      .buildAndAddToParser(argParser);
412      keyStorePasswordFile =
413              FileBasedArgument.builder(OPTION_LONG_KEYSTORE_PWD_FILE)
414                      .shortIdentifier(OPTION_SHORT_KEYSTORE_PWD_FILE)
415                      .description(INFO_DESCRIPTION_KEYSTOREPASSWORD_FILE.get())
416                      .valuePlaceholder(INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get())
417                      .buildAndAddToParser(argParser);
418      certNickname =
419              StringArgument.builder("certNickname")
420                      .shortIdentifier('N')
421                      .description(INFO_DESCRIPTION_CERT_NICKNAME.get())
422                      .valuePlaceholder(INFO_NICKNAME_PLACEHOLDER.get())
423                      .buildAndAddToParser(argParser);
424      trustStorePath =
425              StringArgument.builder(OPTION_LONG_TRUSTSTOREPATH)
426                      .shortIdentifier(OPTION_SHORT_TRUSTSTOREPATH)
427                      .description(INFO_DESCRIPTION_TRUSTSTOREPATH.get())
428                      .valuePlaceholder(INFO_TRUSTSTOREPATH_PLACEHOLDER.get())
429                      .buildAndAddToParser(argParser);
430      trustStorePassword =
431              StringArgument.builder(OPTION_LONG_TRUSTSTORE_PWD)
432                      .description(INFO_DESCRIPTION_TRUSTSTOREPASSWORD.get())
433                      .valuePlaceholder(INFO_TRUSTSTORE_PWD_PLACEHOLDER.get())
434                      .buildAndAddToParser(argParser);
435      trustStorePasswordFile =
436              FileBasedArgument.builder(OPTION_LONG_TRUSTSTORE_PWD_FILE)
437                      .shortIdentifier(OPTION_SHORT_TRUSTSTORE_PWD_FILE)
438                      .description(INFO_DESCRIPTION_TRUSTSTOREPASSWORD_FILE.get())
439                      .valuePlaceholder(INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER.get())
440                      .buildAndAddToParser(argParser);
441      deleteSubtree =
442              BooleanArgument.builder("deleteSubtree")
443                      .shortIdentifier('x')
444                      .description(INFO_DELETE_DESCRIPTION_DELETE_SUBTREE.get())
445                      .buildAndAddToParser(argParser);
446      controlStr =
447              StringArgument.builder("control")
448                      .shortIdentifier('J')
449                      .description(INFO_DESCRIPTION_CONTROLS.get())
450                      .multiValued()
451                      .valuePlaceholder(INFO_LDAP_CONTROL_PLACEHOLDER.get())
452                      .buildAndAddToParser(argParser);
453      version =
454              IntegerArgument.builder(OPTION_LONG_PROTOCOL_VERSION)
455                      .shortIdentifier(OPTION_SHORT_PROTOCOL_VERSION)
456                      .description(INFO_DESCRIPTION_VERSION.get())
457                      .defaultValue(3)
458                      .valuePlaceholder(INFO_PROTOCOL_VERSION_PLACEHOLDER.get())
459                      .buildAndAddToParser(argParser);
460        connectTimeout =
461                IntegerArgument.builder(OPTION_LONG_CONNECT_TIMEOUT)
462                        .description(INFO_DESCRIPTION_CONNECTION_TIMEOUT.get())
463                        .lowerBound(0)
464                        .defaultValue(CliConstants.DEFAULT_LDAP_CONNECT_TIMEOUT)
465                        .valuePlaceholder(INFO_TIMEOUT_PLACEHOLDER.get())
466                        .buildAndAddToParser(argParser);
467      encodingStr =
468              StringArgument.builder(OPTION_LONG_ENCODING)
469                      .shortIdentifier('i')
470                      .description(INFO_DESCRIPTION_ENCODING.get())
471                      .valuePlaceholder(INFO_ENCODING_PLACEHOLDER.get())
472                      .buildAndAddToParser(argParser);
473      continueOnError =
474              BooleanArgument.builder("continueOnError")
475                      .shortIdentifier('c')
476                      .description(INFO_DESCRIPTION_CONTINUE_ON_ERROR.get())
477                      .buildAndAddToParser(argParser);
478      noop =
479              BooleanArgument.builder(OPTION_LONG_DRYRUN)
480                      .shortIdentifier(OPTION_SHORT_DRYRUN)
481                      .description(INFO_DESCRIPTION_NOOP.get())
482                      .buildAndAddToParser(argParser);
483
484      verbose = verboseArgument();
485      argParser.addArgument(verbose);
486
487      showUsage = showUsageArgument();
488      argParser.addArgument(showUsage);
489      argParser.setUsageArgument(showUsage, out);
490    } catch (ArgumentException ae)
491    {
492      printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
493      return CLIENT_SIDE_PARAM_ERROR;
494    }
495
496    // Parse the command-line arguments provided to this program.
497    try
498    {
499      argParser.parseArguments(args);
500    }
501    catch (ArgumentException ae)
502    {
503      argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
504      return CLIENT_SIDE_PARAM_ERROR;
505    }
506
507    // If we should just display usage or version information,
508    // then it has already been done so just exit.
509    if (argParser.usageOrVersionDisplayed())
510    {
511      return 0;
512    }
513
514    if (bindPassword.isPresent() && bindPasswordFile.isPresent())
515    {
516      printWrappedText(err, conflictingArgsErrorMessage(bindPassword, bindPasswordFile));
517      return CLIENT_SIDE_PARAM_ERROR;
518    }
519
520
521    String hostNameValue = hostName.getValue();
522    int portNumber = 389;
523    try
524    {
525      portNumber = port.getIntValue();
526    } catch(ArgumentException ae)
527    {
528      logger.traceException(ae);
529      argParser.displayMessageAndUsageReference(err, ae.getMessageObject());
530      return CLIENT_SIDE_PARAM_ERROR;
531    }
532
533    try
534    {
535      int versionNumber = version.getIntValue();
536      if(versionNumber != 2 && versionNumber != 3)
537      {
538        printWrappedText(err, ERR_DESCRIPTION_INVALID_VERSION.get(versionNumber));
539        return CLIENT_SIDE_PARAM_ERROR;
540      }
541      connectionOptions.setVersionNumber(versionNumber);
542    } catch(ArgumentException ae)
543    {
544      logger.traceException(ae);
545      argParser.displayMessageAndUsageReference(err, ae.getMessageObject());
546      return CLIENT_SIDE_PARAM_ERROR;
547    }
548
549    String bindDNValue = bindDN.getValue();
550    String fileNameValue = filename.getValue();
551    String bindPasswordValue;
552    try
553    {
554      bindPasswordValue = getPasswordValue(
555          bindPassword, bindPasswordFile, bindDNValue, out, err);
556    }
557    catch (Exception ex)
558    {
559      logger.traceException(ex);
560      printWrappedText(err, ex.getMessage());
561      return CLIENT_SIDE_PARAM_ERROR;
562    }
563
564    String keyStorePathValue = keyStorePath.getValue();
565    String trustStorePathValue = trustStorePath.getValue();
566
567    String keyStorePasswordValue = null;
568    if (keyStorePassword.isPresent())
569    {
570      keyStorePasswordValue = keyStorePassword.getValue();
571    }
572    else if (keyStorePasswordFile.isPresent())
573    {
574      keyStorePasswordValue = keyStorePasswordFile.getValue();
575    }
576
577    String trustStorePasswordValue = null;
578    if (trustStorePassword.isPresent())
579    {
580      trustStorePasswordValue = trustStorePassword.getValue();
581    }
582    else if (trustStorePasswordFile.isPresent())
583    {
584      trustStorePasswordValue = trustStorePasswordFile.getValue();
585    }
586
587    deleteOptions.setShowOperations(noop.isPresent());
588    deleteOptions.setVerbose(verbose.isPresent());
589    deleteOptions.setContinueOnError(continueOnError.isPresent());
590    deleteOptions.setEncoding(encodingStr.getValue());
591    deleteOptions.setDeleteSubtree(deleteSubtree.isPresent());
592
593    if(controlStr.isPresent())
594    {
595      for (String ctrlString : controlStr.getValues())
596      {
597        Control ctrl = LDAPToolUtils.getControl(ctrlString, err);
598        if(ctrl == null)
599        {
600          printWrappedText(err, ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString));
601          return CLIENT_SIDE_PARAM_ERROR;
602        }
603        deleteOptions.getControls().add(ctrl);
604      }
605    }
606
607    if(deleteOptions.getDeleteSubtree())
608    {
609      Control control = new SubtreeDeleteControl(false);
610      deleteOptions.getControls().add(control);
611    }
612
613    ArrayList<String> trailingArgs = argParser.getTrailingArguments();
614    dnStrings.addAll(trailingArgs);
615
616    // Set the connection options.
617    // Parse the SASL properties.
618    connectionOptions.setSASLExternal(saslExternal.isPresent());
619    if(saslOptions.isPresent())
620    {
621      for (String saslOption : saslOptions.getValues())
622      {
623        boolean val = saslOption.startsWith("mech=")
624            ? connectionOptions.setSASLMechanism(saslOption)
625            : connectionOptions.addSASLProperty(saslOption);
626        if (!val)
627        {
628          return CLIENT_SIDE_PARAM_ERROR;
629        }
630      }
631    }
632    connectionOptions.setUseSSL(useSSL.isPresent());
633    connectionOptions.setStartTLS(startTLS.isPresent());
634
635    if(connectionOptions.useSASLExternal())
636    {
637      if(!connectionOptions.useSSL() && !connectionOptions.useStartTLS())
638      {
639        printWrappedText(err, ERR_TOOL_SASLEXTERNAL_NEEDS_SSL_OR_TLS.get());
640        return CLIENT_SIDE_PARAM_ERROR;
641      }
642      if(keyStorePathValue == null)
643      {
644        printWrappedText(err, ERR_TOOL_SASLEXTERNAL_NEEDS_KEYSTORE.get());
645        return CLIENT_SIDE_PARAM_ERROR;
646      }
647    }
648
649    LDAPDelete ldapDelete = null;
650    try
651    {
652      if (initializeServer)
653      {
654        // Bootstrap and initialize directory data structures.
655        EmbeddedUtils.initializeForClientUse();
656      }
657
658      // Connect to the specified host with the supplied userDN and password.
659      SSLConnectionFactory sslConnectionFactory = null;
660      if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
661      {
662        String clientAlias;
663        if (certNickname.isPresent())
664        {
665          clientAlias = certNickname.getValue();
666        }
667        else
668        {
669          clientAlias = null;
670        }
671
672        sslConnectionFactory = new SSLConnectionFactory();
673        sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
674                                  keyStorePasswordValue, clientAlias,
675                                  trustStorePathValue, trustStorePasswordValue);
676        connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
677      }
678
679      AtomicInteger nextMessageID = new AtomicInteger(1);
680      connection = new LDAPConnection(hostNameValue, portNumber,
681                                      connectionOptions, out, err);
682      int timeout = connectTimeout.getIntValue();
683      connection.connectToHost(bindDNValue, bindPasswordValue, nextMessageID,
684          timeout);
685
686      ldapDelete = new LDAPDelete(nextMessageID, out, err);
687      if(fileNameValue == null && dnStrings.isEmpty())
688      {
689        // Read from stdin.
690        rdr = new InputStreamReader(System.in);
691      } else if(fileNameValue != null)
692      {
693        rdr = new FileReader(fileNameValue);
694      }
695
696      if(rdr != null)
697      {
698        ldapDelete.readAndExecute(connection, rdr, deleteOptions);
699      } else
700      {
701        ldapDelete.readAndExecute(connection, dnStrings, deleteOptions);
702      }
703    } catch(LDAPException le)
704    {
705      logger.traceException(le);
706      LDAPToolUtils.printErrorMessage(err, le.getMessageObject(),
707                                      le.getResultCode(),
708                                      le.getErrorMessage(),
709                                      le.getMatchedDN());
710      return le.getResultCode();
711    } catch(LDAPConnectionException lce)
712    {
713      logger.traceException(lce);
714      LDAPToolUtils.printErrorMessage(err, lce.getMessageObject(),
715                                      lce.getResultCode(),
716                                      lce.getErrorMessage(),
717                                      lce.getMatchedDN());
718      return lce.getResultCode();
719    }
720    catch(ArgumentException e)
721    {
722      argParser.displayMessageAndUsageReference(err, e.getMessageObject());
723      return 1;
724    }
725    catch (Exception e)
726    {
727      logger.traceException(e);
728      printWrappedText(err, e.getMessage());
729      return 1;
730    } finally
731    {
732      if(connection != null)
733      {
734        if (ldapDelete == null)
735        {
736          connection.close(null);
737        }
738        else
739        {
740          connection.close(ldapDelete.nextMessageID);
741        }
742      }
743    }
744    return 0;
745  }
746
747}
748