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