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.IOException;
033    import java.io.OutputStream;
034    import java.io.PrintStream;
035    import java.util.ArrayList;
036    import java.util.HashSet;
037    import java.util.LinkedHashSet;
038    import java.util.LinkedList;
039    import java.util.StringTokenizer;
040    import java.util.concurrent.atomic.AtomicInteger;
041    
042    import org.opends.server.controls.AccountUsableResponseControl;
043    import org.opends.server.controls.EntryChangeNotificationControl;
044    import org.opends.server.controls.MatchedValuesControl;
045    import org.opends.server.controls.MatchedValuesFilter;
046    import org.opends.server.controls.PagedResultsControl;
047    import org.opends.server.controls.PersistentSearchChangeType;
048    import org.opends.server.controls.PersistentSearchControl;
049    import org.opends.server.controls.ServerSideSortRequestControl;
050    import org.opends.server.controls.ServerSideSortResponseControl;
051    import org.opends.server.controls.VLVRequestControl;
052    import org.opends.server.controls.VLVResponseControl;
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    import org.opends.server.protocols.asn1.ASN1Element;
063    import org.opends.server.protocols.asn1.ASN1Exception;
064    import org.opends.server.protocols.asn1.ASN1OctetString;
065    import org.opends.server.protocols.asn1.ASN1Sequence;
066    import org.opends.server.protocols.ldap.LDAPAttribute;
067    import org.opends.server.protocols.ldap.LDAPControl;
068    import org.opends.server.protocols.ldap.LDAPFilter;
069    import org.opends.server.protocols.ldap.LDAPMessage;
070    import org.opends.server.protocols.ldap.LDAPResultCode;
071    import org.opends.server.protocols.ldap.SearchRequestProtocolOp;
072    import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp;
073    import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
074    import org.opends.server.protocols.ldap.SearchResultReferenceProtocolOp;
075    import org.opends.server.types.*;
076    
077    import static org.opends.server.loggers.debug.DebugLogger.*;
078    import org.opends.server.loggers.debug.DebugTracer;
079    import static org.opends.messages.ToolMessages.*;
080    import static org.opends.server.protocols.ldap.LDAPConstants.*;
081    import static org.opends.server.protocols.ldap.LDAPResultCode.*;
082    import static org.opends.server.util.ServerConstants.*;
083    import static org.opends.server.util.StaticUtils.*;
084    import static org.opends.server.tools.ToolConstants.*;
085    
086    
087    
088    
089    /**
090     * This class provides a tool that can be used to issue search requests to the
091     * Directory Server.
092     */
093    public class LDAPSearch
094    {
095      /**
096       * The tracer object for the debug logger.
097       */
098      private static final DebugTracer TRACER = getTracer();
099    
100      /**
101       * The fully-qualified name of this class.
102       */
103      private static final String CLASS_NAME = "org.opends.server.tools.LDAPSearch";
104    
105    
106    
107      // The set of response controls for the search.
108      private ArrayList<LDAPControl> responseControls;
109    
110      // The message ID counter to use for requests.
111      private AtomicInteger nextMessageID;
112    
113      // The print stream to use for standard error.
114      private PrintStream err;
115    
116      // The print stream to use for standard output.
117      private PrintStream out;
118    
119    
120    
121      /**
122       * Constructor for the LDAPSearch object.
123       *
124       * @param  nextMessageID  The message ID counter to use for requests.
125       * @param  out            The print stream to use for standard output.
126       * @param  err            The print stream to use for standard error.
127       */
128      public LDAPSearch(AtomicInteger nextMessageID, PrintStream out,
129                        PrintStream err)
130      {
131        this.nextMessageID = nextMessageID;
132        this.out           = out;
133        this.err           = err;
134        responseControls   = new ArrayList<LDAPControl>();
135      }
136    
137    
138      /**
139       * Execute the search based on the specified input parameters.
140       *
141       * @param connection     The connection to use for the search.
142       * @param baseDN         The base DN for the search request.
143       * @param filters        The filters to use for the results.
144       * @param attributes     The attributes to return in the results.
145       * @param searchOptions  The constraints for the search.
146       * @param wrapColumn     The column at which to wrap long lines.
147       *
148       * @return  The number of matching entries returned by the server.  If there
149       *          were multiple search filters provided, then this will be the
150       *          total number of matching entries for all searches.
151       *
152       * @throws  IOException  If a problem occurs while attempting to communicate
153       *                       with the Directory Server.
154       *
155       * @throws  LDAPException  If the Directory Server returns an error response.
156       */
157      public int executeSearch(LDAPConnection connection, String baseDN,
158                               ArrayList<LDAPFilter> filters,
159                               LinkedHashSet<String> attributes,
160                               LDAPSearchOptions searchOptions,
161                               int wrapColumn )
162             throws IOException, LDAPException
163      {
164        int matchingEntries = 0;
165    
166        for (LDAPFilter filter: filters)
167        {
168          ASN1OctetString asn1OctetStr = new ASN1OctetString(baseDN);
169    
170          SearchRequestProtocolOp protocolOp =
171            new SearchRequestProtocolOp(asn1OctetStr,
172                                        searchOptions.getSearchScope(),
173                                        searchOptions.getDereferencePolicy(),
174                                        searchOptions.getSizeLimit(),
175                                        searchOptions.getTimeLimit(),
176                                  false, filter, attributes);
177          if(!searchOptions.showOperations())
178          {
179            try
180            {
181              boolean typesOnly = searchOptions.getTypesOnly();
182              LDAPMessage message = new LDAPMessage(nextMessageID.getAndIncrement(),
183                                                    protocolOp,
184                                                    searchOptions.getControls());
185              connection.getLDAPWriter().writeMessage(message);
186    
187              byte opType;
188              do
189              {
190                int resultCode = 0;
191                Message errorMessage = null;
192                DN matchedDN = null;
193                LDAPMessage responseMessage =
194                     connection.getLDAPReader().readMessage();
195                responseControls = responseMessage.getControls();
196    
197    
198                opType = responseMessage.getProtocolOpType();
199                switch(opType)
200                {
201                  case OP_TYPE_SEARCH_RESULT_ENTRY:
202                    for (LDAPControl c : responseControls)
203                    {
204                      if (c.getOID().equals(OID_ENTRY_CHANGE_NOTIFICATION))
205                      {
206                        try
207                        {
208                          EntryChangeNotificationControl ecn =
209                               EntryChangeNotificationControl.decodeControl(
210                                    c.getControl());
211    
212                          out.println(INFO_LDAPSEARCH_PSEARCH_CHANGE_TYPE.get(
213                                  ecn.getChangeType().toString()));
214                          DN previousDN = ecn.getPreviousDN();
215                          if (previousDN != null)
216                          {
217    
218                            out.println(INFO_LDAPSEARCH_PSEARCH_PREVIOUS_DN.get(
219                                    previousDN.toString()));
220                          }
221                        } catch (Exception e) {}
222                      }
223                      else if (c.getOID().equals(OID_ACCOUNT_USABLE_CONTROL))
224                      {
225                        try
226                        {
227                          AccountUsableResponseControl acrc =
228                               AccountUsableResponseControl.decodeControl(
229                                    c.getControl());
230    
231                          out.println(INFO_LDAPSEARCH_ACCTUSABLE_HEADER.get());
232                          if (acrc.isUsable())
233                          {
234    
235                            out.println(INFO_LDAPSEARCH_ACCTUSABLE_IS_USABLE.get());
236                            if (acrc.getSecondsBeforeExpiration() > 0)
237                            {
238                              int timeToExp = acrc.getSecondsBeforeExpiration();
239                              Message timeToExpStr = secondsToTimeString(timeToExp);
240    
241                              out.println(
242                                   INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_EXPIRATION.
243                                           get(timeToExpStr));
244                            }
245                          }
246                          else
247                          {
248    
249                            out.println(
250                                    INFO_LDAPSEARCH_ACCTUSABLE_NOT_USABLE.get());
251                            if (acrc.isInactive())
252                            {
253    
254                              out.println(
255                                   INFO_LDAPSEARCH_ACCTUSABLE_ACCT_INACTIVE.get());
256                            }
257                            if (acrc.isReset())
258                            {
259                              out.println(
260                                      INFO_LDAPSEARCH_ACCTUSABLE_PW_RESET.get());
261                            }
262                            if (acrc.isExpired())
263                            {
264    
265                              out.println(
266                                      INFO_LDAPSEARCH_ACCTUSABLE_PW_EXPIRED.get());
267    
268                              if (acrc.getRemainingGraceLogins() > 0)
269                              {
270    
271                                out.println(
272                                        INFO_LDAPSEARCH_ACCTUSABLE_REMAINING_GRACE
273                                             .get(acrc.getRemainingGraceLogins()));
274                              }
275                            }
276                            if (acrc.isLocked())
277                            {
278    
279                              out.println(INFO_LDAPSEARCH_ACCTUSABLE_LOCKED.get());
280                              if (acrc.getSecondsBeforeUnlock() > 0)
281                              {
282                                int timeToUnlock = acrc.getSecondsBeforeUnlock();
283                                Message timeToUnlockStr =
284                                            secondsToTimeString(timeToUnlock);
285    
286                                out.println(
287                                        INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_UNLOCK
288                                                .get(timeToUnlockStr));
289                              }
290                            }
291                          }
292                        } catch (Exception e) {}
293                      }
294                    }
295    
296                    SearchResultEntryProtocolOp searchEntryOp =
297                         responseMessage.getSearchResultEntryProtocolOp();
298                    StringBuilder sb = new StringBuilder();
299                    toLDIF(searchEntryOp, sb, wrapColumn, typesOnly);
300                    out.print(sb.toString());
301                    matchingEntries++;
302                    break;
303    
304                  case OP_TYPE_SEARCH_RESULT_REFERENCE:
305                    SearchResultReferenceProtocolOp searchRefOp =
306                         responseMessage.getSearchResultReferenceProtocolOp();
307                    out.println(searchRefOp.toString());
308                    break;
309    
310                  case OP_TYPE_SEARCH_RESULT_DONE:
311                    SearchResultDoneProtocolOp searchOp =
312                         responseMessage.getSearchResultDoneProtocolOp();
313                    resultCode = searchOp.getResultCode();
314                    errorMessage = searchOp.getErrorMessage();
315                    matchedDN = searchOp.getMatchedDN();
316    
317                    for (LDAPControl c : responseMessage.getControls())
318                    {
319                      if (c.getOID().equals(OID_SERVER_SIDE_SORT_RESPONSE_CONTROL))
320                      {
321                        try
322                        {
323                          ServerSideSortResponseControl sortResponse =
324                               ServerSideSortResponseControl.decodeControl(
325                                    c.getControl());
326                          int rc = sortResponse.getResultCode();
327                          if (rc != LDAPResultCode.SUCCESS)
328                          {
329                            Message msg   = WARN_LDAPSEARCH_SORT_ERROR.get(
330                                    LDAPResultCode.toString(rc));
331                            err.println(msg);
332                          }
333                        }
334                        catch (Exception e)
335                        {
336                          Message msg   =
337                                  WARN_LDAPSEARCH_CANNOT_DECODE_SORT_RESPONSE.get(
338                                          getExceptionMessage(e));
339                          err.println(msg);
340                        }
341                      }
342                      else if (c.getOID().equals(OID_VLV_RESPONSE_CONTROL))
343                      {
344                        try
345                        {
346                          VLVResponseControl vlvResponse =
347                               VLVResponseControl.decodeControl(c.getControl());
348                          int rc = vlvResponse.getVLVResultCode();
349                          if (rc == LDAPResultCode.SUCCESS)
350                          {
351                            Message msg = INFO_LDAPSEARCH_VLV_TARGET_OFFSET.get(
352                                    vlvResponse.getTargetPosition());
353                            out.println(msg);
354    
355    
356                            msg = INFO_LDAPSEARCH_VLV_CONTENT_COUNT.get(
357                                    vlvResponse.getContentCount());
358                            out.println(msg);
359                          }
360                          else
361                          {
362                            Message msg = WARN_LDAPSEARCH_VLV_ERROR.get(
363                                    LDAPResultCode.toString(rc));
364                            err.println(msg);
365                          }
366                        }
367                        catch (Exception e)
368                        {
369                          Message msg   =
370                                  WARN_LDAPSEARCH_CANNOT_DECODE_VLV_RESPONSE.get(
371                                          getExceptionMessage(e));
372                          err.println(msg);
373                        }
374                      }
375                    }
376    
377                    break;
378                  default:
379                    // FIXME - throw exception?
380                    Message msg = INFO_SEARCH_OPERATION_INVALID_PROTOCOL.get(
381                            String.valueOf(opType));
382                    err.println(wrapText(msg, MAX_LINE_WIDTH));
383                    break;
384                }
385    
386                if(resultCode != SUCCESS && resultCode != REFERRAL)
387                {
388                  Message msg = INFO_OPERATION_FAILED.get("SEARCH");
389                  throw new LDAPException(resultCode, errorMessage, msg,
390                                          matchedDN, null);
391                }
392                else if (errorMessage != null)
393                {
394                  out.println();
395                  out.println(wrapText(errorMessage, MAX_LINE_WIDTH));
396                }
397    
398              } while(opType != OP_TYPE_SEARCH_RESULT_DONE);
399    
400            } catch(ASN1Exception ae)
401            {
402              if (debugEnabled())
403              {
404                TRACER.debugCaught(DebugLogLevel.ERROR, ae);
405              }
406              throw new IOException(ae.getMessage());
407            }
408          }
409        }
410    
411        if (searchOptions.countMatchingEntries())
412        {
413          Message message =
414                  INFO_LDAPSEARCH_MATCHING_ENTRY_COUNT.get(matchingEntries);
415          out.println(message);
416          out.println();
417        }
418        return matchingEntries;
419      }
420    
421      /**
422       * Appends an LDIF representation of the entry to the provided buffer.
423       *
424       * @param  entry       The entry to be written as LDIF.
425       * @param  buffer      The buffer to which the entry should be appended.
426       * @param  wrapColumn  The column at which long lines should be wrapped.
427       * @param  typesOnly   Indicates whether to include only attribute types
428       *                     without values.
429       */
430      public void toLDIF(SearchResultEntryProtocolOp entry, StringBuilder buffer,
431                         int wrapColumn, boolean typesOnly)
432      {
433        // Add the DN to the buffer.
434        String dnString = entry.getDN().toString();
435        int    colsRemaining;
436        if (needsBase64Encoding(dnString))
437        {
438          dnString = Base64.encode(getBytes(dnString));
439          buffer.append("dn:: ");
440    
441          colsRemaining = wrapColumn - 5;
442        }
443        else
444        {
445          buffer.append("dn: ");
446    
447          colsRemaining = wrapColumn - 4;
448        }
449    
450        int dnLength = dnString.length();
451        if ((dnLength <= colsRemaining) || (colsRemaining <= 0))
452        {
453          buffer.append(dnString);
454          buffer.append(EOL);
455        }
456        else
457        {
458          buffer.append(dnString.substring(0, colsRemaining));
459          buffer.append(EOL);
460    
461          int startPos = colsRemaining;
462          while ((dnLength - startPos) > (wrapColumn - 1))
463          {
464            buffer.append(" ");
465            buffer.append(dnString.substring(startPos, (startPos+wrapColumn-1)));
466    
467            buffer.append(EOL);
468    
469            startPos += (wrapColumn-1);
470          }
471    
472          if (startPos < dnLength)
473          {
474            buffer.append(" ");
475            buffer.append(dnString.substring(startPos));
476            buffer.append(EOL);
477          }
478        }
479    
480    
481        LinkedList<LDAPAttribute> attributes = entry.getAttributes();
482        // Add the attributes to the buffer.
483        for (LDAPAttribute a : attributes)
484        {
485          String name       = a.getAttributeType();
486          int    nameLength = name.length();
487    
488    
489          if(typesOnly)
490          {
491              buffer.append(name);
492              buffer.append(EOL);
493          } else
494          {
495            for (ASN1OctetString v : a.getValues())
496            {
497              String valueString;
498              if (needsBase64Encoding(v.value()))
499              {
500                valueString = Base64.encode(v.value());
501                buffer.append(name);
502                buffer.append(":: ");
503    
504                colsRemaining = wrapColumn - nameLength - 3;
505              } else
506              {
507                valueString = v.stringValue();
508                buffer.append(name);
509                buffer.append(": ");
510    
511                colsRemaining = wrapColumn - nameLength - 2;
512              }
513    
514              int valueLength = valueString.length();
515              if ((valueLength <= colsRemaining) || (colsRemaining <= 0))
516              {
517                buffer.append(valueString);
518                buffer.append(EOL);
519              } else
520              {
521                buffer.append(valueString.substring(0, colsRemaining));
522                buffer.append(EOL);
523    
524                int startPos = colsRemaining;
525                while ((valueLength - startPos) > (wrapColumn - 1))
526                {
527                  buffer.append(" ");
528                  buffer.append(valueString.substring(startPos,
529                                                    (startPos+wrapColumn-1)));
530                  buffer.append(EOL);
531    
532                  startPos += (wrapColumn-1);
533                }
534    
535                if (startPos < valueLength)
536                {
537                  buffer.append(" ");
538                  buffer.append(valueString.substring(startPos));
539                  buffer.append(EOL);
540                }
541              }
542            }
543          }
544        }
545    
546    
547        // Make sure to add an extra blank line to ensure that there will be one
548        // between this entry and the next.
549        buffer.append(EOL);
550      }
551    
552      /**
553       * Retrieves the set of response controls included in the last search result
554       * done message.
555       *
556       * @return  The set of response controls included in the last search result
557       *          done message.
558       */
559      public ArrayList<LDAPControl> getResponseControls()
560      {
561        return responseControls;
562      }
563    
564      /**
565       * The main method for LDAPSearch tool.
566       *
567       * @param  args  The command-line arguments provided to this program.
568       */
569    
570      public static void main(String[] args)
571      {
572        int retCode = mainSearch(args, true, System.out, System.err);
573    
574        if(retCode != 0)
575        {
576          System.exit(filterExitCode(retCode));
577        }
578      }
579    
580      /**
581       * Parses the provided command-line arguments and uses that information to
582       * run the ldapsearch tool.
583       *
584       * @param  args  The command-line arguments provided to this program.
585       *
586       * @return The error code.
587       */
588    
589      public static int mainSearch(String[] args)
590      {
591        return mainSearch(args, true, System.out, System.err);
592      }
593    
594      /**
595       * Parses the provided command-line arguments and uses that information to
596       * run the ldapsearch tool.
597       *
598       * @param  args              The command-line arguments provided to this
599       *                           program.
600       * @param  initializeServer  Indicates whether to initialize the server.
601       * @param  outStream         The output stream to use for standard output, or
602       *                           <CODE>null</CODE> if standard output is not
603       *                           needed.
604       * @param  errStream         The output stream to use for standard error, or
605       *                           <CODE>null</CODE> if standard error is not
606       *                           needed.
607       *
608       * @return The error code.
609       */
610    
611      public static int mainSearch(String[] args, boolean initializeServer,
612                                   OutputStream outStream, OutputStream errStream)
613      {
614        PrintStream out;
615        if (outStream == null)
616        {
617          out = NullOutputStream.printStream();
618        }
619        else
620        {
621          out = new PrintStream(outStream);
622        }
623    
624        PrintStream err;
625        if (errStream == null)
626        {
627          err = NullOutputStream.printStream();
628        }
629        else
630        {
631          err = new PrintStream(errStream);
632        }
633    
634        LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
635        LDAPSearchOptions searchOptions = new LDAPSearchOptions();
636        LDAPConnection connection = null;
637        ArrayList<LDAPFilter> filters = new ArrayList<LDAPFilter>();
638        LinkedHashSet<String> attributes = new LinkedHashSet<String>();
639    
640        BooleanArgument   continueOnError          = null;
641        BooleanArgument   countEntries             = null;
642        BooleanArgument   dontWrap                 = null;
643        BooleanArgument   noop                     = null;
644        BooleanArgument   reportAuthzID            = null;
645        BooleanArgument   saslExternal             = null;
646        BooleanArgument   showUsage                = null;
647        BooleanArgument   trustAll                 = null;
648        BooleanArgument   usePasswordPolicyControl = null;
649        BooleanArgument   useSSL                   = null;
650        BooleanArgument   startTLS                 = null;
651        BooleanArgument   typesOnly                = null;
652        BooleanArgument   verbose                  = null;
653        FileBasedArgument bindPasswordFile         = null;
654        FileBasedArgument keyStorePasswordFile     = null;
655        FileBasedArgument trustStorePasswordFile   = null;
656        IntegerArgument   port                     = null;
657        IntegerArgument   simplePageSize           = null;
658        IntegerArgument   sizeLimit                = null;
659        IntegerArgument   timeLimit                = null;
660        IntegerArgument   version                  = null;
661        StringArgument    assertionFilter          = null;
662        StringArgument    baseDN                   = null;
663        StringArgument    bindDN                   = null;
664        StringArgument    bindPassword             = null;
665        StringArgument    certNickname             = null;
666        StringArgument    controlStr               = null;
667        StringArgument    dereferencePolicy        = null;
668        StringArgument    encodingStr              = null;
669        StringArgument    filename                 = null;
670        StringArgument    hostName                 = null;
671        StringArgument    keyStorePath             = null;
672        StringArgument    keyStorePassword         = null;
673        StringArgument    matchedValuesFilter      = null;
674        StringArgument    proxyAuthzID             = null;
675        StringArgument    pSearchInfo              = null;
676        StringArgument    saslOptions              = null;
677        StringArgument    searchScope              = null;
678        StringArgument    sortOrder                = null;
679        StringArgument    trustStorePath           = null;
680        StringArgument    trustStorePassword       = null;
681        StringArgument    vlvDescriptor            = null;
682        StringArgument    effectiveRightsUser      = null;
683        StringArgument    effectiveRightsAttrs     = null;
684        StringArgument    propertiesFileArgument   = null;
685        BooleanArgument   noPropertiesFileArgument = null;
686    
687    
688        // Create the command-line argument parser for use with this program.
689        Message toolDescription = INFO_LDAPSEARCH_TOOL_DESCRIPTION.get();
690        ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
691                                                      false, true, 0, 0,
692                                                      "[filter] [attributes ...]");
693    
694        try
695        {
696          propertiesFileArgument = new StringArgument("propertiesFilePath",
697              null, OPTION_LONG_PROP_FILE_PATH,
698              false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
699              INFO_DESCRIPTION_PROP_FILE_PATH.get());
700          argParser.addArgument(propertiesFileArgument);
701          argParser.setFilePropertiesArgument(propertiesFileArgument);
702    
703          noPropertiesFileArgument = new BooleanArgument(
704              "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
705              INFO_DESCRIPTION_NO_PROP_FILE.get());
706          argParser.addArgument(noPropertiesFileArgument);
707          argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
708    
709          hostName = new StringArgument("host", OPTION_SHORT_HOST,
710                                        OPTION_LONG_HOST, false, false, true,
711                                        INFO_HOST_PLACEHOLDER.get(), "localhost",
712                                        null,
713                                        INFO_DESCRIPTION_HOST.get());
714          hostName.setPropertyName(OPTION_LONG_HOST);
715          argParser.addArgument(hostName);
716    
717          port = new IntegerArgument("port", OPTION_SHORT_PORT,
718                                     OPTION_LONG_PORT, false, false, true,
719                                     INFO_PORT_PLACEHOLDER.get(), 389, null,
720                                     INFO_DESCRIPTION_PORT.get());
721          port.setPropertyName(OPTION_LONG_PORT);
722          argParser.addArgument(port);
723    
724          useSSL = new BooleanArgument("useSSL", OPTION_SHORT_USE_SSL,
725                                       OPTION_LONG_USE_SSL,
726                                       INFO_DESCRIPTION_USE_SSL.get());
727          useSSL.setPropertyName(OPTION_LONG_USE_SSL);
728          argParser.addArgument(useSSL);
729    
730          startTLS = new BooleanArgument("startTLS", OPTION_SHORT_START_TLS,
731                                        OPTION_LONG_START_TLS,
732                                        INFO_DESCRIPTION_START_TLS.get());
733          startTLS.setPropertyName(OPTION_LONG_START_TLS);
734          argParser.addArgument(startTLS);
735    
736          bindDN = new StringArgument("bindDN", OPTION_SHORT_BINDDN,
737                                      OPTION_LONG_BINDDN, false, false, true,
738                                      INFO_BINDDN_PLACEHOLDER.get(), null, null,
739                                      INFO_DESCRIPTION_BINDDN.get());
740          bindDN.setPropertyName(OPTION_LONG_BINDDN);
741          argParser.addArgument(bindDN);
742    
743          bindPassword = new StringArgument("bindPassword", OPTION_SHORT_BINDPWD,
744                                            OPTION_LONG_BINDPWD,
745                                            false, false, true,
746                                            INFO_BINDPWD_PLACEHOLDER.get(),
747                                            null, null,
748                                            INFO_DESCRIPTION_BINDPASSWORD.get());
749          bindPassword.setPropertyName(OPTION_LONG_BINDPWD);
750          argParser.addArgument(bindPassword);
751    
752          bindPasswordFile =
753               new FileBasedArgument("bindPasswordFile", OPTION_SHORT_BINDPWD_FILE,
754                                     OPTION_LONG_BINDPWD_FILE,
755                                     false, false,
756                                     INFO_BINDPWD_FILE_PLACEHOLDER.get(), null,
757                                     null, INFO_DESCRIPTION_BINDPASSWORDFILE.get());
758          bindPasswordFile.setPropertyName(OPTION_LONG_BINDPWD_FILE);
759          argParser.addArgument(bindPasswordFile);
760    
761          baseDN = new StringArgument("baseDN", OPTION_SHORT_BASEDN,
762                                      OPTION_LONG_BASEDN, true, false, true,
763                                      INFO_BASEDN_PLACEHOLDER.get(), null, null,
764                                      INFO_SEARCH_DESCRIPTION_BASEDN.get());
765          baseDN.setPropertyName(OPTION_LONG_BASEDN);
766          argParser.addArgument(baseDN);
767    
768          searchScope = new StringArgument(
769                  "searchScope", 's', "searchScope", false,
770                  false, true, INFO_SEARCH_SCOPE_PLACEHOLDER.get(), null, null,
771                  INFO_SEARCH_DESCRIPTION_SEARCH_SCOPE.get());
772          searchScope.setPropertyName("searchScope");
773          argParser.addArgument(searchScope);
774    
775          filename = new StringArgument("filename", OPTION_SHORT_FILENAME,
776                                        OPTION_LONG_FILENAME, false, false,
777                                        true, INFO_FILE_PLACEHOLDER.get(), null,
778                                        null,
779                                        INFO_SEARCH_DESCRIPTION_FILENAME.get());
780          searchScope.setPropertyName(OPTION_LONG_FILENAME);
781          argParser.addArgument(filename);
782    
783          saslExternal = new BooleanArgument(
784                  "useSASLExternal", 'r',
785                  "useSASLExternal",
786                  INFO_DESCRIPTION_USE_SASL_EXTERNAL.get());
787          saslExternal.setPropertyName("useSASLExternal");
788          argParser.addArgument(saslExternal);
789    
790          saslOptions = new StringArgument("saslOption", OPTION_SHORT_SASLOPTION,
791                                           OPTION_LONG_SASLOPTION, false,
792                                           true, true,
793                                           INFO_SASL_OPTION_PLACEHOLDER.get(), null,
794                                           null,
795                                           INFO_DESCRIPTION_SASL_PROPERTIES.get());
796          saslOptions.setPropertyName(OPTION_LONG_SASLOPTION);
797          argParser.addArgument(saslOptions);
798    
799          trustAll = new BooleanArgument("trustAll", 'X', "trustAll",
800                                        INFO_DESCRIPTION_TRUSTALL.get());
801          trustAll.setPropertyName("trustAll");
802          argParser.addArgument(trustAll);
803    
804          keyStorePath = new StringArgument("keyStorePath",
805                                      OPTION_SHORT_KEYSTOREPATH,
806                                      OPTION_LONG_KEYSTOREPATH, false, false, true,
807                                      INFO_KEYSTOREPATH_PLACEHOLDER.get(), null,
808                                      null,
809                                      INFO_DESCRIPTION_KEYSTOREPATH.get());
810          keyStorePath.setPropertyName(OPTION_LONG_KEYSTOREPATH);
811          argParser.addArgument(keyStorePath);
812    
813          keyStorePassword = new StringArgument("keyStorePassword",
814                                      OPTION_SHORT_KEYSTORE_PWD,
815                                      OPTION_LONG_KEYSTORE_PWD, false, false,
816                                      true, INFO_KEYSTORE_PWD_PLACEHOLDER.get(),
817                                      null, null,
818                                      INFO_DESCRIPTION_KEYSTOREPASSWORD.get());
819          keyStorePassword.setPropertyName(OPTION_LONG_KEYSTORE_PWD);
820          argParser.addArgument(keyStorePassword);
821    
822          keyStorePasswordFile =
823               new FileBasedArgument("keystorepasswordfile",
824                                     OPTION_SHORT_KEYSTORE_PWD_FILE,
825                                     OPTION_LONG_KEYSTORE_PWD_FILE,
826                                     false, false,
827                                     INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get(),
828                                     null, null,
829                                     INFO_DESCRIPTION_KEYSTOREPASSWORD_FILE.get());
830          keyStorePasswordFile.setPropertyName(OPTION_LONG_KEYSTORE_PWD_FILE);
831          argParser.addArgument(keyStorePasswordFile);
832    
833          certNickname = new StringArgument(
834                  "certnickname", OPTION_SHORT_CERT_NICKNAME,
835                  OPTION_LONG_CERT_NICKNAME,
836                  false, false, true, INFO_NICKNAME_PLACEHOLDER.get(), null,
837                  null, INFO_DESCRIPTION_CERT_NICKNAME.get());
838          certNickname.setPropertyName(OPTION_LONG_CERT_NICKNAME);
839          argParser.addArgument(certNickname);
840    
841          trustStorePath = new StringArgument("trustStorePath",
842                                      OPTION_SHORT_TRUSTSTOREPATH,
843                                      OPTION_LONG_TRUSTSTOREPATH,
844                                      false, false, true,
845                                      INFO_TRUSTSTOREPATH_PLACEHOLDER.get(), null,
846                                      null,
847                                      INFO_DESCRIPTION_TRUSTSTOREPATH.get());
848          trustStorePath.setPropertyName(OPTION_LONG_TRUSTSTOREPATH);
849          argParser.addArgument(trustStorePath);
850    
851          trustStorePassword =
852               new StringArgument("trustStorePassword", null,
853                                  OPTION_LONG_TRUSTSTORE_PWD,
854                                  false, false, true,
855                                  INFO_TRUSTSTORE_PWD_PLACEHOLDER.get(),
856                                  null,
857                                  null, INFO_DESCRIPTION_TRUSTSTOREPASSWORD.get());
858          trustStorePassword.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD);
859          argParser.addArgument(trustStorePassword);
860    
861          trustStorePasswordFile =
862               new FileBasedArgument(
863                       "truststorepasswordfile",
864                       OPTION_SHORT_TRUSTSTORE_PWD_FILE,
865                       OPTION_LONG_TRUSTSTORE_PWD_FILE, false, false,
866                       INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER.get(), null, null,
867                       INFO_DESCRIPTION_TRUSTSTOREPASSWORD_FILE.get());
868          trustStorePasswordFile.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD_FILE);
869          argParser.addArgument(trustStorePasswordFile);
870    
871          proxyAuthzID = new StringArgument("proxy_authzid",
872                                            OPTION_SHORT_PROXYAUTHID,
873                                            OPTION_LONG_PROXYAUTHID, false,
874                                            false, true,
875                                            INFO_PROXYAUTHID_PLACEHOLDER.get(),
876                                            null, null,
877                                            INFO_DESCRIPTION_PROXY_AUTHZID.get());
878          proxyAuthzID.setPropertyName(OPTION_LONG_PROXYAUTHID);
879          argParser.addArgument(proxyAuthzID);
880    
881          reportAuthzID = new BooleanArgument(
882                  "reportauthzid", 'E', OPTION_LONG_REPORT_AUTHZ_ID,
883                  INFO_DESCRIPTION_REPORT_AUTHZID.get());
884          reportAuthzID.setPropertyName(OPTION_LONG_REPORT_AUTHZ_ID);
885          argParser.addArgument(reportAuthzID);
886    
887          usePasswordPolicyControl = new BooleanArgument(
888                  "usepwpolicycontrol", null,
889                  OPTION_LONG_USE_PW_POLICY_CTL,
890                  INFO_DESCRIPTION_USE_PWP_CONTROL.get());
891          usePasswordPolicyControl.setPropertyName(OPTION_LONG_USE_PW_POLICY_CTL);
892          argParser.addArgument(usePasswordPolicyControl);
893    
894          pSearchInfo = new StringArgument("psearchinfo", 'C', "persistentSearch",
895                                 false, false, true,
896                                 INFO_PSEARCH_PLACEHOLDER.get(),
897                                  null, null, INFO_DESCRIPTION_PSEARCH_INFO.get());
898          pSearchInfo.setPropertyName("persistentSearch");
899          argParser.addArgument(pSearchInfo);
900    
901          simplePageSize = new IntegerArgument(
902                  "simplepagesize", null,
903                  "simplePageSize", false, false, true,
904                  INFO_NUM_ENTRIES_PLACEHOLDER.get(), 1000, null, true, 1,
905                  false, 0,
906                  INFO_DESCRIPTION_SIMPLE_PAGE_SIZE.get());
907          simplePageSize.setPropertyName("simplePageSize");
908          argParser.addArgument(simplePageSize);
909    
910          assertionFilter = new StringArgument(
911                  "assertionfilter", null,
912                  OPTION_LONG_ASSERTION_FILE,
913                  false, false,
914                  true, INFO_ASSERTION_FILTER_PLACEHOLDER.get(),
915                  null, null,
916                  INFO_DESCRIPTION_ASSERTION_FILTER.get());
917          assertionFilter.setPropertyName(OPTION_LONG_ASSERTION_FILE);
918          argParser.addArgument(assertionFilter);
919    
920          matchedValuesFilter = new StringArgument(
921                  "matchedvalues", null,
922                  "matchedValuesFilter", false, true, true,
923                  INFO_FILTER_PLACEHOLDER.get(), null, null,
924                  INFO_DESCRIPTION_MATCHED_VALUES_FILTER.get());
925          matchedValuesFilter.setPropertyName("matchedValuesFilter");
926          argParser.addArgument(matchedValuesFilter);
927    
928          sortOrder = new StringArgument(
929                  "sortorder", 'S', "sortOrder", false,
930                  false, true, INFO_SORT_ORDER_PLACEHOLDER.get(), null, null,
931                  INFO_DESCRIPTION_SORT_ORDER.get());
932          sortOrder.setPropertyName("sortOrder");
933          argParser.addArgument(sortOrder);
934    
935          vlvDescriptor =
936               new StringArgument(
937                       "vlvdescriptor", 'G', "virtualListView", false,
938                       false, true,
939                       INFO_VLV_PLACEHOLDER.get(),
940                       null, null, INFO_DESCRIPTION_VLV.get());
941          vlvDescriptor.setPropertyName("virtualListView");
942          argParser.addArgument(vlvDescriptor);
943    
944          controlStr =
945               new StringArgument("control", 'J', "control", false, true, true,
946                        INFO_LDAP_CONTROL_PLACEHOLDER.get(),
947                        null, null, INFO_DESCRIPTION_CONTROLS.get());
948          controlStr.setPropertyName("control");
949          argParser.addArgument(controlStr);
950    
951          effectiveRightsUser =
952                  new StringArgument("effectiveRightsUser",
953                          OPTION_SHORT_EFFECTIVERIGHTSUSER,
954                          OPTION_LONG_EFFECTIVERIGHTSUSER, false, false, true,
955                          INFO_PROXYAUTHID_PLACEHOLDER.get(), null, null,
956                          INFO_DESCRIPTION_EFFECTIVERIGHTS_USER.get( ));
957          effectiveRightsUser.setPropertyName(OPTION_LONG_EFFECTIVERIGHTSUSER);
958          argParser.addArgument(effectiveRightsUser);
959    
960          effectiveRightsAttrs =
961                  new StringArgument("effectiveRightsAttrs",
962                          OPTION_SHORT_EFFECTIVERIGHTSATTR,
963                          OPTION_LONG_EFFECTIVERIGHTSATTR, false, true, true,
964                          INFO_ATTRIBUTE_PLACEHOLDER.get(), null, null,
965                          INFO_DESCRIPTION_EFFECTIVERIGHTS_ATTR.get( ));
966          effectiveRightsAttrs.setPropertyName(OPTION_LONG_EFFECTIVERIGHTSATTR);
967          argParser.addArgument(effectiveRightsAttrs);
968    
969          version = new IntegerArgument("version", OPTION_SHORT_PROTOCOL_VERSION,
970                                        OPTION_LONG_PROTOCOL_VERSION, false, false,
971                                        true,
972                                        INFO_PROTOCOL_VERSION_PLACEHOLDER.get(), 3,
973                                        null, INFO_DESCRIPTION_VERSION.get());
974          version.setPropertyName(OPTION_LONG_PROTOCOL_VERSION);
975          argParser.addArgument(version);
976    
977          encodingStr = new StringArgument("encoding", 'i', "encoding", false,
978                                           false, true,
979                                           INFO_ENCODING_PLACEHOLDER.get(), null,
980                                           null,
981                                           INFO_DESCRIPTION_ENCODING.get());
982          encodingStr.setPropertyName("encoding");
983          argParser.addArgument(encodingStr);
984    
985          dereferencePolicy =
986               new StringArgument("derefpolicy", 'a', "dereferencePolicy", false,
987                                  false, true,
988                                  INFO_DEREFERENCE_POLICE_PLACEHOLDER.get(), null,
989                                  null,
990                                  INFO_SEARCH_DESCRIPTION_DEREFERENCE_POLICY.get());
991          dereferencePolicy.setPropertyName("dereferencePolicy");
992          argParser.addArgument(dereferencePolicy);
993    
994          typesOnly = new BooleanArgument("typesOnly", 'A', "typesOnly",
995                                          INFO_DESCRIPTION_TYPES_ONLY.get());
996          typesOnly.setPropertyName("typesOnly");
997          argParser.addArgument(typesOnly);
998    
999          sizeLimit = new IntegerArgument("sizeLimit", 'z', "sizeLimit", false,
1000                                          false, true,
1001                                          INFO_SIZE_LIMIT_PLACEHOLDER.get(), 0,
1002                                          null,
1003                                          INFO_SEARCH_DESCRIPTION_SIZE_LIMIT.get());
1004          sizeLimit.setPropertyName("sizeLimit");
1005          argParser.addArgument(sizeLimit);
1006    
1007          timeLimit = new IntegerArgument("timeLimit", 'l', "timeLimit", false,
1008                                          false, true,
1009                                          INFO_TIME_LIMIT_PLACEHOLDER.get(), 0,
1010                                          null,
1011                                          INFO_SEARCH_DESCRIPTION_TIME_LIMIT.get());
1012          timeLimit.setPropertyName("timeLimit");
1013          argParser.addArgument(timeLimit);
1014    
1015          dontWrap = new BooleanArgument("dontwrap", 'T',
1016                                         "dontWrap",
1017                                         INFO_DESCRIPTION_DONT_WRAP.get());
1018          dontWrap.setPropertyName("dontWrap");
1019          argParser.addArgument(dontWrap);
1020    
1021          countEntries = new BooleanArgument("countentries", null, "countEntries",
1022                                             INFO_DESCRIPTION_COUNT_ENTRIES.get());
1023          countEntries.setPropertyName("countEntries");
1024          argParser.addArgument(countEntries);
1025    
1026          continueOnError =
1027               new BooleanArgument("continueOnError", 'c', "continueOnError",
1028                                   INFO_DESCRIPTION_CONTINUE_ON_ERROR.get());
1029          continueOnError.setPropertyName("continueOnError");
1030          argParser.addArgument(continueOnError);
1031    
1032          noop = new BooleanArgument("noop", OPTION_SHORT_DRYRUN,
1033              OPTION_LONG_DRYRUN, INFO_DESCRIPTION_NOOP.get());
1034          noop.setPropertyName(OPTION_LONG_DRYRUN);
1035          argParser.addArgument(noop);
1036    
1037          verbose = new BooleanArgument("verbose", 'v', "verbose",
1038                                        INFO_DESCRIPTION_VERBOSE.get());
1039          verbose.setPropertyName("verbose");
1040          argParser.addArgument(verbose);
1041    
1042          showUsage = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
1043                                        OPTION_LONG_HELP,
1044                                        INFO_DESCRIPTION_SHOWUSAGE.get());
1045          argParser.addArgument(showUsage);
1046          argParser.setUsageArgument(showUsage, out);
1047        } catch (ArgumentException ae)
1048        {
1049    
1050          Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
1051    
1052          err.println(wrapText(message, MAX_LINE_WIDTH));
1053          return 1;
1054        }
1055    
1056        // Parse the command-line arguments provided to this program.
1057        try
1058        {
1059          argParser.parseArguments(args);
1060        }
1061        catch (ArgumentException ae)
1062        {
1063    
1064          Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
1065    
1066          err.println(wrapText(message, MAX_LINE_WIDTH));
1067          err.println(argParser.getUsage());
1068          return 1;
1069        }
1070    
1071        // If we should just display usage or version information,
1072        // then print it and exit.
1073        if (argParser.usageOrVersionDisplayed())
1074        {
1075          return 0;
1076        }
1077    
1078        ArrayList<String> filterAndAttributeStrings =
1079          argParser.getTrailingArguments();
1080        if(filterAndAttributeStrings.size() > 0)
1081        {
1082          // the list of trailing arguments should be structured as follow:
1083          // - If a filter file is present, trailing arguments are considered
1084          //   as attributes
1085          // - If filter file is not present, the first trailing argument is
1086          // considered the filter, the other as attributes.
1087          if (! filename.isPresent())
1088          {
1089            String filterString = filterAndAttributeStrings.remove(0);
1090    
1091            try
1092            {
1093              filters.add(LDAPFilter.decode(filterString));
1094            } catch (LDAPException le)
1095            {
1096              if (debugEnabled())
1097              {
1098                TRACER.debugCaught(DebugLogLevel.ERROR, le);
1099              }
1100              err.println(wrapText(le.getMessage(), MAX_LINE_WIDTH));
1101              return 1;
1102            }
1103          }
1104          // The rest are attributes
1105          for(String s : filterAndAttributeStrings)
1106          {
1107            attributes.add(s);
1108          }
1109    
1110        }
1111    
1112        if(bindPassword.isPresent() && bindPasswordFile.isPresent())
1113        {
1114          Message message =
1115                  ERR_TOOL_CONFLICTING_ARGS.get(
1116                          bindPassword.getLongIdentifier(),
1117                          bindPasswordFile.getLongIdentifier());
1118          err.println(wrapText(message, MAX_LINE_WIDTH));
1119          return 1;
1120        }
1121    
1122        if (useSSL.isPresent() && startTLS.isPresent())
1123        {
1124          Message message = ERR_TOOL_CONFLICTING_ARGS.get(
1125                  useSSL.getLongIdentifier(),
1126                  startTLS.getLongIdentifier());
1127          err.println(wrapText(message, MAX_LINE_WIDTH));
1128          return 1;
1129        }
1130    
1131        if (keyStorePassword.isPresent() && keyStorePasswordFile.isPresent())
1132        {
1133          Message message = ERR_TOOL_CONFLICTING_ARGS.get(
1134                  keyStorePassword.getLongIdentifier(),
1135                  keyStorePasswordFile.getLongIdentifier());
1136          err.println(wrapText(message, MAX_LINE_WIDTH));
1137          return 1;
1138        }
1139    
1140        if (trustStorePassword.isPresent() && trustStorePasswordFile.isPresent())
1141        {
1142          Message message = ERR_TOOL_CONFLICTING_ARGS.get(
1143                  trustStorePassword.getLongIdentifier(),
1144                  trustStorePasswordFile.getLongIdentifier());
1145          err.println(wrapText(message, MAX_LINE_WIDTH));
1146          return 1;
1147        }
1148    
1149        String hostNameValue = hostName.getValue();
1150        int portNumber = 389;
1151        try
1152        {
1153          portNumber = port.getIntValue();
1154        } catch(ArgumentException ae)
1155        {
1156          if (debugEnabled())
1157          {
1158            TRACER.debugCaught(DebugLogLevel.ERROR, ae);
1159          }
1160          err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
1161          return 1;
1162        }
1163    
1164        // Read the LDAP version number.
1165        try
1166        {
1167          int versionNumber = version.getIntValue();
1168          if(versionNumber != 2 && versionNumber != 3)
1169          {
1170    
1171            err.println(wrapText(ERR_DESCRIPTION_INVALID_VERSION.get(
1172                    String.valueOf(versionNumber)), MAX_LINE_WIDTH));
1173            return 1;
1174          }
1175          connectionOptions.setVersionNumber(versionNumber);
1176        } catch(ArgumentException ae)
1177        {
1178          if (debugEnabled())
1179          {
1180            TRACER.debugCaught(DebugLogLevel.ERROR, ae);
1181          }
1182          err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
1183          return 1;
1184        }
1185    
1186    
1187        // Indicate whether we should report the authorization ID and/or use the
1188        // password policy control.
1189        connectionOptions.setReportAuthzID(reportAuthzID.isPresent());
1190        connectionOptions.setUsePasswordPolicyControl(
1191             usePasswordPolicyControl.isPresent());
1192    
1193    
1194        String baseDNValue = baseDN.getValue();
1195        String bindDNValue = bindDN.getValue();
1196        String fileNameValue = filename.getValue();
1197        String bindPasswordValue = bindPassword.getValue();
1198        if(bindPasswordValue != null && bindPasswordValue.equals("-"))
1199        {
1200          // read the password from the stdin.
1201          try
1202          {
1203            out.print(INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDNValue));
1204            char[] pwChars = PasswordReader.readPassword();
1205            bindPasswordValue = new String(pwChars);
1206          } catch(Exception ex)
1207          {
1208            if (debugEnabled())
1209            {
1210              TRACER.debugCaught(DebugLogLevel.ERROR, ex);
1211            }
1212            err.println(wrapText(ex.getMessage(), MAX_LINE_WIDTH));
1213            return 1;
1214          }
1215        }
1216        else if(bindPasswordValue == null)
1217        {
1218          // Read from file if it exists.
1219          bindPasswordValue = bindPasswordFile.getValue();
1220        }
1221    
1222        String keyStorePathValue = keyStorePath.getValue();
1223        String trustStorePathValue = trustStorePath.getValue();
1224    
1225        String keyStorePasswordValue = null;
1226        if (keyStorePassword.isPresent())
1227        {
1228          keyStorePasswordValue = keyStorePassword.getValue();
1229        }
1230        else if (keyStorePasswordFile.isPresent())
1231        {
1232          keyStorePasswordValue = keyStorePasswordFile.getValue();
1233        }
1234    
1235        String trustStorePasswordValue = null;
1236        if (trustStorePassword.isPresent())
1237        {
1238          trustStorePasswordValue = trustStorePassword.getValue();
1239        }
1240        else if (trustStorePasswordFile.isPresent())
1241        {
1242          trustStorePasswordValue = trustStorePasswordFile.getValue();
1243        }
1244    
1245        searchOptions.setTypesOnly(typesOnly.isPresent());
1246        searchOptions.setShowOperations(noop.isPresent());
1247        searchOptions.setVerbose(verbose.isPresent());
1248        searchOptions.setContinueOnError(continueOnError.isPresent());
1249        searchOptions.setEncoding(encodingStr.getValue());
1250        searchOptions.setCountMatchingEntries(countEntries.isPresent());
1251        try
1252        {
1253          searchOptions.setTimeLimit(timeLimit.getIntValue());
1254          searchOptions.setSizeLimit(sizeLimit.getIntValue());
1255        } catch(ArgumentException ex1)
1256        {
1257          err.println(wrapText(ex1.getMessage(), MAX_LINE_WIDTH));
1258          return 1;
1259        }
1260        boolean val = searchOptions.setSearchScope(searchScope.getValue(), err);
1261        if(val == false)
1262        {
1263          return 1;
1264        }
1265        val = searchOptions.setDereferencePolicy(dereferencePolicy.getValue(), err);
1266        if(val == false)
1267        {
1268          return 1;
1269        }
1270    
1271        if(controlStr.isPresent())
1272        {
1273          for (String ctrlString : controlStr.getValues())
1274          {
1275            LDAPControl ctrl = LDAPToolUtils.getControl(ctrlString, err);
1276            if(ctrl == null)
1277            {
1278              Message message = ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString);
1279              err.println(wrapText(message, MAX_LINE_WIDTH));
1280              err.println(argParser.getUsage());
1281              return 1;
1282            }
1283            searchOptions.getControls().add(ctrl);
1284          }
1285        }
1286    
1287        if(effectiveRightsUser.isPresent()) {
1288          String authzID=effectiveRightsUser.getValue();
1289          if (!authzID.startsWith("dn:")) {
1290            Message message = ERR_EFFECTIVERIGHTS_INVALID_AUTHZID.get(authzID);
1291            err.println(wrapText(message, MAX_LINE_WIDTH));
1292            err.println(argParser.getUsage());
1293            return 1;
1294          }
1295          ASN1OctetString v=null;
1296          ASN1OctetString effectiveRightsUserVal =
1297                  new ASN1OctetString(authzID);
1298          ASN1Sequence sequence=null;
1299          ArrayList<ASN1Element> attrElements =
1300                  new ArrayList<ASN1Element>();
1301          for(String a : effectiveRightsAttrs.getValues())
1302            attrElements.add(new ASN1OctetString(a));
1303          ASN1Sequence attrSeq=new ASN1Sequence(attrElements);
1304          ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
1305          elements.add(effectiveRightsUserVal);
1306          elements.add(attrSeq);
1307          sequence= new ASN1Sequence(elements);
1308          LDAPControl effectiveRightsControl =
1309                  new LDAPControl(OID_GET_EFFECTIVE_RIGHTS, false,
1310                          new ASN1OctetString(sequence.encode()));
1311          searchOptions.getControls().add(effectiveRightsControl);
1312        }
1313    
1314        if (proxyAuthzID.isPresent())
1315        {
1316          ASN1OctetString proxyValue = new ASN1OctetString(proxyAuthzID.getValue());
1317    
1318          LDAPControl proxyControl =
1319            new LDAPControl(OID_PROXIED_AUTH_V2, true, proxyValue);
1320          searchOptions.getControls().add(proxyControl);
1321        }
1322    
1323        if (pSearchInfo.isPresent())
1324        {
1325          String infoString = toLowerCase(pSearchInfo.getValue().trim());
1326          HashSet<PersistentSearchChangeType> changeTypes =
1327               new HashSet<PersistentSearchChangeType>();
1328          boolean changesOnly = true;
1329          boolean returnECs = true;
1330    
1331          StringTokenizer tokenizer = new StringTokenizer(infoString, ":");
1332    
1333          if (! tokenizer.hasMoreTokens())
1334          {
1335            Message message = ERR_PSEARCH_MISSING_DESCRIPTOR.get();
1336            err.println(wrapText(message, MAX_LINE_WIDTH));
1337            return 1;
1338          }
1339          else
1340          {
1341            String token = tokenizer.nextToken();
1342            if (! token.equals("ps"))
1343            {
1344              Message message = ERR_PSEARCH_DOESNT_START_WITH_PS.get(
1345                      String.valueOf(infoString));
1346              err.println(wrapText(message, MAX_LINE_WIDTH));
1347              return 1;
1348            }
1349          }
1350    
1351          if (tokenizer.hasMoreTokens())
1352          {
1353            StringTokenizer st = new StringTokenizer(tokenizer.nextToken(), ", ");
1354            while (st.hasMoreTokens())
1355            {
1356              String token = st.nextToken();
1357              if (token.equals("add"))
1358              {
1359                changeTypes.add(PersistentSearchChangeType.ADD);
1360              }
1361              else if (token.equals("delete") || token.equals("del"))
1362              {
1363                changeTypes.add(PersistentSearchChangeType.DELETE);
1364              }
1365              else if (token.equals("modify") || token.equals("mod"))
1366              {
1367                changeTypes.add(PersistentSearchChangeType.MODIFY);
1368              }
1369              else if (token.equals("modifydn") || token.equals("moddn") ||
1370                       token.equals("modrdn"))
1371              {
1372                changeTypes.add(PersistentSearchChangeType.MODIFY_DN);
1373              }
1374              else if (token.equals("any") || token.equals("all"))
1375              {
1376                changeTypes.add(PersistentSearchChangeType.ADD);
1377                changeTypes.add(PersistentSearchChangeType.DELETE);
1378                changeTypes.add(PersistentSearchChangeType.MODIFY);
1379                changeTypes.add(PersistentSearchChangeType.MODIFY_DN);
1380              }
1381              else
1382              {
1383                Message message =
1384                        ERR_PSEARCH_INVALID_CHANGE_TYPE.get(String.valueOf(token));
1385                err.println(wrapText(message, MAX_LINE_WIDTH));
1386                return 1;
1387              }
1388            }
1389          }
1390    
1391          if (changeTypes.isEmpty())
1392          {
1393            changeTypes.add(PersistentSearchChangeType.ADD);
1394            changeTypes.add(PersistentSearchChangeType.DELETE);
1395            changeTypes.add(PersistentSearchChangeType.MODIFY);
1396            changeTypes.add(PersistentSearchChangeType.MODIFY_DN);
1397          }
1398    
1399          if (tokenizer.hasMoreTokens())
1400          {
1401            String token = tokenizer.nextToken();
1402            if (token.equals("1") || token.equals("true") || token.equals("yes"))
1403            {
1404              changesOnly = true;
1405            }
1406            else if (token.equals("0") || token.equals("false") ||
1407                     token.equals("no"))
1408            {
1409              changesOnly = false;
1410            }
1411            else
1412            {
1413              Message message = ERR_PSEARCH_INVALID_CHANGESONLY.get(
1414                      String.valueOf(token));
1415              err.println(wrapText(message, MAX_LINE_WIDTH));
1416              return 1;
1417            }
1418          }
1419    
1420          if (tokenizer.hasMoreTokens())
1421          {
1422            String token = tokenizer.nextToken();
1423            if (token.equals("1") || token.equals("true") || token.equals("yes"))
1424            {
1425              returnECs = true;
1426            }
1427            else if (token.equals("0") || token.equals("false") ||
1428                     token.equals("no"))
1429            {
1430              returnECs = false;
1431            }
1432            else
1433            {
1434              Message message = ERR_PSEARCH_INVALID_RETURN_ECS.get(
1435                      String.valueOf(token));
1436              err.println(wrapText(message, MAX_LINE_WIDTH));
1437              return 1;
1438            }
1439          }
1440    
1441          PersistentSearchControl psearchControl =
1442               new PersistentSearchControl(changeTypes, changesOnly, returnECs);
1443          searchOptions.getControls().add(new LDAPControl(psearchControl));
1444        }
1445    
1446        if (assertionFilter.isPresent())
1447        {
1448          String filterString = assertionFilter.getValue();
1449          LDAPFilter filter;
1450          try
1451          {
1452            filter = LDAPFilter.decode(filterString);
1453    
1454            // FIXME -- Change this to the correct OID when the official one is
1455            //          assigned.
1456            LDAPControl assertionControl =
1457                 new LDAPControl(OID_LDAP_ASSERTION, true,
1458                                 new ASN1OctetString(filter.encode().encode()));
1459            searchOptions.getControls().add(assertionControl);
1460          }
1461          catch (LDAPException le)
1462          {
1463            Message message = ERR_LDAP_ASSERTION_INVALID_FILTER.get(
1464                    le.getMessage());
1465            err.println(wrapText(message, MAX_LINE_WIDTH));
1466            return 1;
1467          }
1468        }
1469    
1470        if (matchedValuesFilter.isPresent())
1471        {
1472          LinkedList<String> mvFilterStrings = matchedValuesFilter.getValues();
1473          ArrayList<MatchedValuesFilter> mvFilters =
1474               new ArrayList<MatchedValuesFilter>();
1475          for (String s : mvFilterStrings)
1476          {
1477            try
1478            {
1479              LDAPFilter f = LDAPFilter.decode(s);
1480              mvFilters.add(MatchedValuesFilter.createFromLDAPFilter(f));
1481            }
1482            catch (LDAPException le)
1483            {
1484              Message message = ERR_LDAP_MATCHEDVALUES_INVALID_FILTER.get(
1485                      le.getMessage());
1486              err.println(wrapText(message, MAX_LINE_WIDTH));
1487              return 1;
1488            }
1489          }
1490    
1491          MatchedValuesControl mvc = new MatchedValuesControl(true, mvFilters);
1492          searchOptions.getControls().add(new LDAPControl(mvc));
1493        }
1494    
1495        if (sortOrder.isPresent())
1496        {
1497          try
1498          {
1499            searchOptions.getControls().add(
1500                 new LDAPControl(new ServerSideSortRequestControl(
1501                                          sortOrder.getValue())));
1502          }
1503          catch (LDAPException le)
1504          {
1505            Message message = ERR_LDAP_SORTCONTROL_INVALID_ORDER.get(
1506                    le.getErrorMessage());
1507            err.println(wrapText(message, MAX_LINE_WIDTH));
1508            return 1;
1509          }
1510        }
1511    
1512        if (vlvDescriptor.isPresent())
1513        {
1514          if (! sortOrder.isPresent())
1515          {
1516            Message message = ERR_LDAPSEARCH_VLV_REQUIRES_SORT.get(
1517                    vlvDescriptor.getLongIdentifier(),
1518                    sortOrder.getLongIdentifier());
1519            err.println(wrapText(message, MAX_LINE_WIDTH));
1520            return 1;
1521          }
1522    
1523          StringTokenizer tokenizer =
1524               new StringTokenizer(vlvDescriptor.getValue(), ":");
1525          int numTokens = tokenizer.countTokens();
1526          if (numTokens == 3)
1527          {
1528            try
1529            {
1530              int beforeCount = Integer.parseInt(tokenizer.nextToken());
1531              int afterCount  = Integer.parseInt(tokenizer.nextToken());
1532              ASN1OctetString assertionValue =
1533                   new ASN1OctetString(tokenizer.nextToken());
1534              searchOptions.getControls().add(
1535                   new LDAPControl(new VLVRequestControl(beforeCount, afterCount,
1536                                                         assertionValue)));
1537            }
1538            catch (Exception e)
1539            {
1540              Message message = ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get();
1541              err.println(wrapText(message, MAX_LINE_WIDTH));
1542              return 1;
1543            }
1544          }
1545          else if (numTokens == 4)
1546          {
1547            try
1548            {
1549              int beforeCount  = Integer.parseInt(tokenizer.nextToken());
1550              int afterCount   = Integer.parseInt(tokenizer.nextToken());
1551              int offset       = Integer.parseInt(tokenizer.nextToken());
1552              int contentCount = Integer.parseInt(tokenizer.nextToken());
1553              searchOptions.getControls().add(
1554                   new LDAPControl(new VLVRequestControl(beforeCount, afterCount,
1555                                                         offset, contentCount)));
1556            }
1557            catch (Exception e)
1558            {
1559              Message message = ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get();
1560              err.println(wrapText(message, MAX_LINE_WIDTH));
1561              return 1;
1562            }
1563          }
1564          else
1565          {
1566            Message message = ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get();
1567            err.println(wrapText(message, MAX_LINE_WIDTH));
1568            return 1;
1569          }
1570        }
1571    
1572        // Set the connection options.
1573        connectionOptions.setSASLExternal(saslExternal.isPresent());
1574        if(saslOptions.isPresent())
1575        {
1576          LinkedList<String> values = saslOptions.getValues();
1577          for(String saslOption : values)
1578          {
1579            if(saslOption.startsWith("mech="))
1580            {
1581              boolean mechValue = connectionOptions.setSASLMechanism(saslOption);
1582              if(mechValue == false)
1583              {
1584                return 1;
1585              }
1586            } else
1587            {
1588              boolean propValue = connectionOptions.addSASLProperty(saslOption);
1589              if(propValue == false)
1590              {
1591                return 1;
1592              }
1593            }
1594          }
1595        }
1596        connectionOptions.setUseSSL(useSSL.isPresent());
1597        connectionOptions.setStartTLS(startTLS.isPresent());
1598    
1599        if(connectionOptions.useSASLExternal())
1600        {
1601          if(!connectionOptions.useSSL() && !connectionOptions.useStartTLS())
1602          {
1603            Message message = ERR_TOOL_SASLEXTERNAL_NEEDS_SSL_OR_TLS.get();
1604            err.println(wrapText(message, MAX_LINE_WIDTH));
1605            return 1;
1606          }
1607          if(keyStorePathValue == null)
1608          {
1609            Message message = ERR_TOOL_SASLEXTERNAL_NEEDS_KEYSTORE.get();
1610            err.println(wrapText(message, MAX_LINE_WIDTH));
1611            return 1;
1612          }
1613        }
1614    
1615        connectionOptions.setVerbose(verbose.isPresent());
1616    
1617        // Read the filter strings.
1618        if(fileNameValue != null)
1619        {
1620          BufferedReader in = null;
1621          try
1622          {
1623            in = new BufferedReader(new FileReader(fileNameValue));
1624            String line = null;
1625    
1626            while ((line = in.readLine()) != null)
1627            {
1628              if(line.trim().equals(""))
1629              {
1630                // ignore empty lines.
1631                continue;
1632              }
1633              LDAPFilter ldapFilter = LDAPFilter.decode(line);
1634              filters.add(ldapFilter);
1635            }
1636          } catch(Exception e)
1637          {
1638            if (debugEnabled())
1639            {
1640              TRACER.debugCaught(DebugLogLevel.ERROR, e);
1641            }
1642            err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
1643            return 1;
1644          }
1645          finally
1646          {
1647            if(in != null)
1648            {
1649              try
1650              {
1651               in.close();
1652              } catch (IOException ioe) {}
1653            }
1654          }
1655    
1656        }
1657    
1658        if(filters.isEmpty())
1659        {
1660    
1661          err.println(wrapText(ERR_SEARCH_NO_FILTERS.get(), MAX_LINE_WIDTH));
1662          err.println(argParser.getUsage());
1663          return 1;
1664        }
1665    
1666        int wrapColumn = 80;
1667        if (dontWrap.isPresent())
1668        {
1669          wrapColumn = 0;
1670        }
1671    
1672        LDAPSearch ldapSearch = null;
1673        try
1674        {
1675          if (initializeServer)
1676          {
1677            // Bootstrap and initialize directory data structures.
1678            EmbeddedUtils.initializeForClientUse();
1679          }
1680    
1681          // Connect to the specified host with the supplied userDN and password.
1682          SSLConnectionFactory sslConnectionFactory = null;
1683          if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
1684          {
1685            String clientAlias;
1686            if (certNickname.isPresent())
1687            {
1688              clientAlias = certNickname.getValue();
1689            }
1690            else
1691            {
1692              clientAlias = null;
1693            }
1694    
1695            sslConnectionFactory = new SSLConnectionFactory();
1696            sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
1697                                      keyStorePasswordValue, clientAlias,
1698                                      trustStorePathValue, trustStorePasswordValue);
1699            connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
1700          }
1701    
1702          if (noop.isPresent())
1703          {
1704            // We don't actually need to open a connection or perform the search,
1705            // so we're done.  We should return 0 to either mean that the processing
1706            // was successful or that there were no matching entries, based on
1707            // countEntries.isPresent() (but in either case the return value should
1708            // be zero).
1709            return 0;
1710          }
1711    
1712          AtomicInteger nextMessageID = new AtomicInteger(1);
1713          connection = new LDAPConnection(hostNameValue, portNumber,
1714                                          connectionOptions, out, err);
1715          connection.connectToHost(bindDNValue, bindPasswordValue, nextMessageID);
1716    
1717          int matchingEntries = 0;
1718          if (simplePageSize.isPresent())
1719          {
1720            if (filters.size() > 1)
1721            {
1722              Message message = ERR_PAGED_RESULTS_REQUIRES_SINGLE_FILTER.get();
1723              throw new LDAPException(CLIENT_SIDE_PARAM_ERROR, message);
1724            }
1725    
1726            int pageSize = simplePageSize.getIntValue();
1727            ASN1OctetString cookieValue = new ASN1OctetString();
1728            ArrayList<LDAPControl> origControls = searchOptions.getControls();
1729    
1730            while (true)
1731            {
1732              ArrayList<LDAPControl> newControls =
1733                   new ArrayList<LDAPControl>(origControls.size()+1);
1734              newControls.addAll(origControls);
1735              newControls.add(new LDAPControl(
1736                   new PagedResultsControl(true, pageSize, cookieValue)));
1737              searchOptions.setControls(newControls);
1738    
1739              ldapSearch = new LDAPSearch(nextMessageID, out, err);
1740              matchingEntries += ldapSearch.executeSearch(connection, baseDNValue,
1741                                                          filters, attributes,
1742                                                          searchOptions,
1743                                                          wrapColumn);
1744    
1745              ArrayList<LDAPControl> responseControls =
1746                   ldapSearch.getResponseControls();
1747              boolean responseFound = false;
1748              for (LDAPControl c  :responseControls)
1749              {
1750                if (c.getOID().equals(OID_PAGED_RESULTS_CONTROL))
1751                {
1752                  try
1753                  {
1754                    PagedResultsControl control =
1755                         new PagedResultsControl(c.isCritical(), c.getValue());
1756                    responseFound = true;
1757                    cookieValue = control.getCookie();
1758                    break;
1759                  }
1760                  catch (LDAPException le)
1761                  {
1762                    Message message =
1763                        ERR_PAGED_RESULTS_CANNOT_DECODE.get(le.getMessage());
1764                    throw new LDAPException(
1765                            CLIENT_SIDE_DECODING_ERROR, message, le);
1766                  }
1767                }
1768              }
1769    
1770              if (! responseFound)
1771              {
1772                Message message = ERR_PAGED_RESULTS_RESPONSE_NOT_FOUND.get();
1773                throw new LDAPException(CLIENT_SIDE_CONTROL_NOT_FOUND, message);
1774              }
1775              else if (cookieValue.value().length == 0)
1776              {
1777                break;
1778              }
1779            }
1780          }
1781          else
1782          {
1783            ldapSearch = new LDAPSearch(nextMessageID, out, err);
1784            matchingEntries = ldapSearch.executeSearch(connection, baseDNValue,
1785                                                       filters, attributes,
1786                                                       searchOptions, wrapColumn);
1787          }
1788    
1789          if (countEntries.isPresent())
1790          {
1791            return matchingEntries;
1792          }
1793          else
1794          {
1795            return 0;
1796          }
1797    
1798        } catch(LDAPException le)
1799        {
1800          if (debugEnabled())
1801          {
1802            TRACER.debugCaught(DebugLogLevel.ERROR, le);
1803          }
1804    
1805          LDAPToolUtils.printErrorMessage(err,
1806                                          le.getMessageObject(),
1807                                          le.getResultCode(),
1808                                          le.getErrorMessage(),
1809                                          le.getMatchedDN());
1810          int code = le.getResultCode();
1811          return code;
1812        } catch(LDAPConnectionException lce)
1813        {
1814          if (debugEnabled())
1815          {
1816            TRACER.debugCaught(DebugLogLevel.ERROR, lce);
1817          }
1818          LDAPToolUtils.printErrorMessage(err,
1819                                          lce.getMessageObject(),
1820                                          lce.getResultCode(),
1821                                          lce.getErrorMessage(),
1822                                          lce.getMatchedDN());
1823          int code = lce.getResultCode();
1824          return code;
1825        } catch(Exception e)
1826        {
1827          if (debugEnabled())
1828          {
1829            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1830          }
1831          err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
1832          return 1;
1833        } finally
1834        {
1835          if(connection != null)
1836          {
1837            if (ldapSearch == null)
1838            {
1839              connection.close(null);
1840            }
1841            else
1842            {
1843              connection.close(ldapSearch.nextMessageID);
1844            }
1845          }
1846        }
1847      }
1848    }
1849