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