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