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