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.FileInputStream;
031 import java.io.InputStream;
032 import java.io.IOException;
033 import java.io.OutputStream;
034 import java.io.PrintStream;
035 import java.util.ArrayList;
036 import java.util.LinkedList;
037 import java.util.List;
038 import java.util.StringTokenizer;
039 import java.util.concurrent.atomic.AtomicInteger;
040
041 import org.opends.server.protocols.asn1.ASN1Element;
042 import org.opends.server.protocols.asn1.ASN1Exception;
043 import org.opends.server.protocols.asn1.ASN1OctetString;
044 import org.opends.server.protocols.asn1.ASN1Sequence;
045 import org.opends.server.protocols.ldap.AddRequestProtocolOp;
046 import org.opends.server.protocols.ldap.AddResponseProtocolOp;
047 import org.opends.server.protocols.ldap.DeleteRequestProtocolOp;
048 import org.opends.server.protocols.ldap.DeleteResponseProtocolOp;
049 import org.opends.server.protocols.ldap.LDAPAttribute;
050 import org.opends.server.protocols.ldap.LDAPControl;
051 import org.opends.server.protocols.ldap.LDAPFilter;
052 import org.opends.server.protocols.ldap.LDAPMessage;
053 import org.opends.server.protocols.ldap.ModifyRequestProtocolOp;
054 import org.opends.server.protocols.ldap.ModifyResponseProtocolOp;
055 import org.opends.server.protocols.ldap.ModifyDNRequestProtocolOp;
056 import org.opends.server.protocols.ldap.ModifyDNResponseProtocolOp;
057 import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
058 import org.opends.server.protocols.ldap.ProtocolOp;
059 import org.opends.server.types.Attribute;
060 import org.opends.server.types.DebugLogLevel;
061 import org.opends.server.types.DN;
062 import org.opends.server.types.LDAPException;
063 import org.opends.server.types.LDIFImportConfig;
064 import org.opends.server.types.NullOutputStream;
065 import org.opends.server.types.RawAttribute;
066 import org.opends.server.types.RawModification;
067 import org.opends.server.util.AddChangeRecordEntry;
068 import org.opends.server.util.ChangeRecordEntry;
069 import org.opends.server.util.EmbeddedUtils;
070 import org.opends.server.util.LDIFException;
071 import org.opends.server.util.LDIFReader;
072 import org.opends.server.util.ModifyChangeRecordEntry;
073 import org.opends.server.util.ModifyDNChangeRecordEntry;
074 import org.opends.server.util.PasswordReader;
075 import org.opends.server.util.args.ArgumentException;
076 import org.opends.server.util.args.ArgumentParser;
077 import org.opends.server.util.args.BooleanArgument;
078 import org.opends.server.util.args.FileBasedArgument;
079 import org.opends.server.util.args.IntegerArgument;
080 import org.opends.server.util.args.StringArgument;
081
082 import static org.opends.server.loggers.debug.DebugLogger.*;
083 import org.opends.server.loggers.debug.DebugTracer;
084 import static org.opends.messages.ToolMessages.*;
085 import static org.opends.server.protocols.ldap.LDAPResultCode.*;
086 import static org.opends.server.util.ServerConstants.*;
087 import static org.opends.server.util.StaticUtils.*;
088 import static org.opends.server.tools.ToolConstants.*;
089
090
091
092 /**
093 * This class provides a tool that can be used to issue modify requests to the
094 * Directory Server.
095 */
096 public class LDAPModify
097 {
098 /**
099 * The tracer object for the debug logger.
100 */
101 private static final DebugTracer TRACER = getTracer();
102
103 /**
104 * The fully-qualified name of this class.
105 */
106 private static final String CLASS_NAME = "org.opends.server.tools.LDAPModify";
107
108 // The message ID counter to use for requests.
109 private AtomicInteger nextMessageID;
110
111 // The print stream to use for standard error.
112 private PrintStream err;
113
114 // The print stream to use for standard output.
115 private PrintStream out;
116
117 // The LDIF file name.
118 private String fileName = null;
119
120 /**
121 * Constructor for the LDAPModify object.
122 *
123 * @param fileName The name of the file containing the LDIF data to use
124 * for the modifications.
125 * @param nextMessageID The message ID counter to use for requests.
126 * @param out The print stream to use for standard output.
127 * @param err The print stream to use for standard error.
128 */
129 public LDAPModify(String fileName, AtomicInteger nextMessageID,
130 PrintStream out, PrintStream err)
131 {
132 if(fileName == null)
133 {
134 this.fileName = "Console";
135 } else
136 {
137 this.fileName = fileName;
138 }
139
140 this.nextMessageID = nextMessageID;
141 this.out = out;
142 this.err = err;
143 }
144
145
146 /**
147 * Read the specified change records from the given input stream
148 * (file or stdin) and execute the given modify request.
149 *
150 * @param connection The connection to use for this modify request.
151 * @param fileNameValue Name of the file from which to read. If null,
152 * input will be read from <code>System.in</code>.
153 * @param modifyOptions The constraints for the modify request.
154 *
155 * @throws IOException If a problem occurs while attempting to communicate
156 * with the Directory Server.
157 *
158 * @throws LDAPException If the Directory Server returns an error response.
159 */
160 public void readAndExecute(LDAPConnection connection, String fileNameValue,
161 LDAPModifyOptions modifyOptions)
162 throws IOException, LDAPException
163 {
164 ArrayList<LDAPControl> controls = modifyOptions.getControls();
165 LDIFReader reader;
166
167 // Create an LDIF import configuration to do this and then get the reader.
168
169 try
170 {
171 InputStream is = System.in;
172 if(fileNameValue != null)
173 {
174 is = new FileInputStream(fileNameValue);
175 }
176
177 LDIFImportConfig importConfig = new LDIFImportConfig(is);
178 reader = new LDIFReader(importConfig);
179 } catch (Exception e)
180 {
181 if (debugEnabled())
182 {
183 TRACER.debugCaught(DebugLogLevel.ERROR, e);
184 }
185 Message message =
186 ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(fileNameValue,
187 e.getLocalizedMessage());
188 throw new IOException(message.toString());
189 }
190
191 while (true)
192 {
193 ChangeRecordEntry entry = null;
194
195 try
196 {
197 entry = reader.readChangeRecord(modifyOptions.getDefaultAdd());
198 } catch (LDIFException le)
199 {
200 if (debugEnabled())
201 {
202 TRACER.debugCaught(DebugLogLevel.ERROR, le);
203 }
204 if (!modifyOptions.continueOnError())
205 {
206 try
207 {
208 reader.close();
209 }
210 catch (Exception e)
211 {
212 if (debugEnabled())
213 {
214 TRACER.debugCaught(DebugLogLevel.ERROR, e);
215 }
216 }
217
218 Message message = ERR_LDIF_FILE_INVALID_LDIF_ENTRY.get(
219 le.getLineNumber(), fileName, String.valueOf(le));
220 throw new IOException(message.toString());
221 }
222 else
223 {
224 Message message = ERR_LDIF_FILE_INVALID_LDIF_ENTRY.get(
225 le.getLineNumber(), fileName, String.valueOf(le));
226 err.println(wrapText(message, MAX_LINE_WIDTH));
227 continue;
228 }
229 } catch (Exception e)
230 {
231 if (debugEnabled())
232 {
233 TRACER.debugCaught(DebugLogLevel.ERROR, e);
234 }
235
236 if (!modifyOptions.continueOnError())
237 {
238 try
239 {
240 reader.close();
241 }
242 catch (Exception e2)
243 {
244 if (debugEnabled())
245 {
246 TRACER.debugCaught(DebugLogLevel.ERROR, e2);
247 }
248 }
249
250 Message message =
251 ERR_LDIF_FILE_READ_ERROR.get(fileName, String.valueOf(e));
252 throw new IOException(message.toString());
253 }
254 else
255 {
256 Message message = ERR_LDIF_FILE_READ_ERROR.get(
257 fileName, String.valueOf(e));
258 err.println(wrapText(message, MAX_LINE_WIDTH));
259 continue;
260 }
261 }
262
263 // If the entry is null, then we have reached the end of the config file.
264 if(entry == null)
265 {
266 try
267 {
268 reader.close();
269 }
270 catch (Exception e)
271 {
272 if (debugEnabled())
273 {
274 TRACER.debugCaught(DebugLogLevel.ERROR, e);
275 }
276 }
277
278 break;
279 }
280
281 ProtocolOp protocolOp = null;
282 ASN1OctetString asn1OctetStr =
283 new ASN1OctetString(entry.getDN().toString());
284
285 String operationType = "";
286 int msgID = 0;
287 switch(entry.getChangeOperationType())
288 {
289 case ADD:
290 operationType = "ADD";
291 AddChangeRecordEntry addEntry = (AddChangeRecordEntry) entry;
292 List<Attribute> attrs = addEntry.getAttributes();
293 ArrayList<RawAttribute> attributes =
294 new ArrayList<RawAttribute>(attrs.size());
295 for(Attribute a : attrs)
296 {
297 attributes.add(new LDAPAttribute(a));
298 }
299 protocolOp = new AddRequestProtocolOp(asn1OctetStr, attributes);
300 out.println(INFO_PROCESSING_OPERATION.get(
301 operationType, String.valueOf(asn1OctetStr)));
302 break;
303 case DELETE:
304 operationType = "DELETE";
305 protocolOp = new DeleteRequestProtocolOp(asn1OctetStr);
306 out.println(INFO_PROCESSING_OPERATION.get(
307 operationType, String.valueOf(asn1OctetStr)));
308 break;
309 case MODIFY:
310 operationType = "MODIFY";
311 ModifyChangeRecordEntry modEntry = (ModifyChangeRecordEntry) entry;
312 ArrayList<RawModification> mods =
313 new ArrayList<RawModification>(modEntry.getModifications());
314 protocolOp = new ModifyRequestProtocolOp(asn1OctetStr, mods);
315 out.println(INFO_PROCESSING_OPERATION.get(
316 operationType, String.valueOf(asn1OctetStr)));
317 break;
318 case MODIFY_DN:
319 operationType = "MODIFY DN";
320 ModifyDNChangeRecordEntry modDNEntry =
321 (ModifyDNChangeRecordEntry) entry;
322 if(modDNEntry.getNewSuperiorDN() != null)
323 {
324 protocolOp = new ModifyDNRequestProtocolOp(asn1OctetStr,
325 new ASN1OctetString(modDNEntry.getNewRDN().toString()),
326 modDNEntry.deleteOldRDN(),
327 new ASN1OctetString(
328 modDNEntry.getNewSuperiorDN().toString()));
329 } else
330 {
331 protocolOp = new ModifyDNRequestProtocolOp(asn1OctetStr,
332 new ASN1OctetString(modDNEntry.getNewRDN().toString()),
333 modDNEntry.deleteOldRDN());
334 }
335
336 out.println(INFO_PROCESSING_OPERATION.get(
337 operationType, String.valueOf(asn1OctetStr)));
338 break;
339 default:
340 break;
341 }
342
343 if(!modifyOptions.showOperations())
344 {
345 LDAPMessage responseMessage = null;
346 try
347 {
348 LDAPMessage message =
349 new LDAPMessage(nextMessageID.getAndIncrement(), protocolOp,
350 controls);
351 connection.getLDAPWriter().writeMessage(message);
352 responseMessage = connection.getLDAPReader().readMessage();
353 } catch(ASN1Exception ae)
354 {
355 if (debugEnabled())
356 {
357 TRACER.debugCaught(DebugLogLevel.ERROR, ae);
358 }
359 Message message = INFO_OPERATION_FAILED.get(operationType);
360 err.println(wrapText(message, MAX_LINE_WIDTH));
361 err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
362 if (!modifyOptions.continueOnError())
363 {
364 throw new IOException(ae.getMessage());
365 }
366 return;
367 }
368
369 int resultCode = 0;
370 Message errorMessage = null;
371 DN matchedDN = null;
372 List<String> referralURLs = null;
373 switch(entry.getChangeOperationType())
374 {
375 case ADD:
376 AddResponseProtocolOp addOp =
377 responseMessage.getAddResponseProtocolOp();
378 resultCode = addOp.getResultCode();
379 errorMessage = addOp.getErrorMessage();
380 matchedDN = addOp.getMatchedDN();
381 referralURLs = addOp.getReferralURLs();
382 break;
383 case DELETE:
384 DeleteResponseProtocolOp delOp =
385 responseMessage.getDeleteResponseProtocolOp();
386 resultCode = delOp.getResultCode();
387 errorMessage = delOp.getErrorMessage();
388 matchedDN = delOp.getMatchedDN();
389 referralURLs = delOp.getReferralURLs();
390 break;
391 case MODIFY:
392 ModifyResponseProtocolOp modOp =
393 responseMessage.getModifyResponseProtocolOp();
394 resultCode = modOp.getResultCode();
395 errorMessage = modOp.getErrorMessage();
396 matchedDN = modOp.getMatchedDN();
397 referralURLs = modOp.getReferralURLs();
398 break;
399 case MODIFY_DN:
400 ModifyDNResponseProtocolOp modDNOp =
401 responseMessage.getModifyDNResponseProtocolOp();
402 resultCode = modDNOp.getResultCode();
403 errorMessage = modDNOp.getErrorMessage();
404 matchedDN = modDNOp.getMatchedDN();
405 referralURLs = modDNOp.getReferralURLs();
406 break;
407 default:
408 break;
409 }
410
411 if(resultCode != SUCCESS && resultCode != REFERRAL)
412 {
413 Message msg = INFO_OPERATION_FAILED.get(operationType);
414
415 if(!modifyOptions.continueOnError())
416 {
417 throw new LDAPException(resultCode, errorMessage, msg,
418 matchedDN, null);
419 } else
420 {
421 LDAPToolUtils.printErrorMessage(err, msg, resultCode, errorMessage,
422 matchedDN);
423 }
424 } else
425 {
426 Message msg = INFO_OPERATION_SUCCESSFUL.get(
427 operationType, String.valueOf(asn1OctetStr));
428 out.println(msg);
429
430 if (errorMessage != null)
431 {
432 out.println(wrapText(errorMessage, MAX_LINE_WIDTH));
433 }
434
435 if (referralURLs != null)
436 {
437 out.println(referralURLs);
438 }
439 }
440
441
442 for (LDAPControl c : responseMessage.getControls())
443 {
444 String oid = c.getOID();
445 if (oid.equals(OID_LDAP_READENTRY_PREREAD))
446 {
447 ASN1OctetString controlValue = c.getValue();
448 if (controlValue == null)
449 {
450
451 err.println(wrapText(
452 ERR_LDAPMODIFY_PREREAD_NO_VALUE.get(),
453 MAX_LINE_WIDTH));
454 continue;
455 }
456
457 SearchResultEntryProtocolOp searchEntry;
458 try
459 {
460 byte[] valueBytes = controlValue.value();
461 ASN1Element valueElement = ASN1Element.decode(valueBytes);
462 searchEntry =
463 SearchResultEntryProtocolOp.decodeSearchEntry(valueElement);
464 }
465 catch (ASN1Exception ae)
466 {
467
468 err.println(wrapText(
469 ERR_LDAPMODIFY_PREREAD_CANNOT_DECODE_VALUE.get(
470 ae.getMessage()),
471 MAX_LINE_WIDTH));
472 continue;
473 }
474 catch (LDAPException le)
475 {
476
477 err.println(wrapText(
478 ERR_LDAPMODIFY_PREREAD_CANNOT_DECODE_VALUE.get(
479 le.getMessage()),
480 MAX_LINE_WIDTH));
481 continue;
482 }
483
484 StringBuilder buffer = new StringBuilder();
485 searchEntry.toLDIF(buffer, 78);
486 out.println(INFO_LDAPMODIFY_PREREAD_ENTRY.get());
487 out.println(buffer);
488 }
489 else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
490 {
491 ASN1OctetString controlValue = c.getValue();
492 if (controlValue == null)
493 {
494
495 err.println(wrapText(
496 ERR_LDAPMODIFY_POSTREAD_NO_VALUE.get(),
497 MAX_LINE_WIDTH));
498 continue;
499 }
500
501 SearchResultEntryProtocolOp searchEntry;
502 try
503 {
504 byte[] valueBytes = controlValue.value();
505 ASN1Element valueElement = ASN1Element.decode(valueBytes);
506 searchEntry =
507 SearchResultEntryProtocolOp.decodeSearchEntry(valueElement);
508 }
509 catch (ASN1Exception ae)
510 {
511
512 err.println(wrapText(
513 ERR_LDAPMODIFY_POSTREAD_CANNOT_DECODE_VALUE.get(
514 ae.getMessage()),
515 MAX_LINE_WIDTH));
516 continue;
517 }
518 catch (LDAPException le)
519 {
520
521 err.println(wrapText(
522 ERR_LDAPMODIFY_POSTREAD_CANNOT_DECODE_VALUE.get(
523 le.getMessage()),
524 MAX_LINE_WIDTH));
525 continue;
526 }
527
528 StringBuilder buffer = new StringBuilder();
529 searchEntry.toLDIF(buffer, 78);
530 out.println(INFO_LDAPMODIFY_POSTREAD_ENTRY.get());
531 out.println(buffer);
532 }
533 }
534 }
535 }
536
537 }
538
539 /**
540 * The main method for LDAPModify tool.
541 *
542 * @param args The command-line arguments provided to this program.
543 */
544
545 public static void main(String[] args)
546 {
547 int retCode = mainModify(args, true, System.out, System.err);
548
549 if(retCode != 0)
550 {
551 System.exit(filterExitCode(retCode));
552 }
553 }
554
555
556 /**
557 * Parses the provided command-line arguments and uses that information to
558 * run the ldapmodify tool.
559 *
560 * @param args The command-line arguments provided to this program.
561 *
562 * @return The error code.
563 */
564
565 public static int mainModify(String[] args)
566 {
567 return mainModify(args, true, System.out, System.err);
568 }
569
570
571 /**
572 * Parses the provided command-line arguments and uses that information to
573 * run the ldapmodify tool.
574 *
575 * @param args The command-line arguments provided to this
576 * program.
577 * @param initializeServer Indicates whether to initialize the server.
578 * @param outStream The output stream to use for standard output, or
579 * <CODE>null</CODE> if standard output is not
580 * needed.
581 * @param errStream The output stream to use for standard error, or
582 * <CODE>null</CODE> if standard error is not
583 * needed.
584 *
585 * @return The error code.
586 */
587
588 public static int mainModify(String[] args, boolean initializeServer,
589 OutputStream outStream, OutputStream errStream)
590 {
591 PrintStream out;
592 if (outStream == null)
593 {
594 out = NullOutputStream.printStream();
595 }
596 else
597 {
598 out = new PrintStream(outStream);
599 }
600
601 PrintStream err;
602 if (errStream == null)
603 {
604 err = NullOutputStream.printStream();
605 }
606 else
607 {
608 err = new PrintStream(errStream);
609 }
610
611
612 LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
613 LDAPModifyOptions modifyOptions = new LDAPModifyOptions();
614 LDAPConnection connection = null;
615
616 BooleanArgument continueOnError = null;
617 BooleanArgument defaultAdd = null;
618 BooleanArgument noop = null;
619 BooleanArgument reportAuthzID = null;
620 BooleanArgument saslExternal = null;
621 BooleanArgument showUsage = null;
622 BooleanArgument startTLS = null;
623 BooleanArgument trustAll = null;
624 BooleanArgument useSSL = null;
625 BooleanArgument verbose = null;
626 FileBasedArgument bindPasswordFile = null;
627 FileBasedArgument keyStorePasswordFile = null;
628 FileBasedArgument trustStorePasswordFile = null;
629 IntegerArgument port = null;
630 IntegerArgument version = null;
631 StringArgument assertionFilter = null;
632 StringArgument bindDN = null;
633 StringArgument bindPassword = null;
634 StringArgument certNickname = null;
635 StringArgument controlStr = null;
636 StringArgument encodingStr = null;
637 StringArgument filename = null;
638 StringArgument hostName = null;
639 StringArgument keyStorePath = null;
640 StringArgument keyStorePassword = null;
641 StringArgument postReadAttributes = null;
642 StringArgument preReadAttributes = null;
643 StringArgument proxyAuthzID = null;
644 StringArgument saslOptions = null;
645 StringArgument trustStorePath = null;
646 StringArgument trustStorePassword = null;
647 StringArgument propertiesFileArgument = null;
648 BooleanArgument noPropertiesFileArgument = null;
649
650 // Create the command-line argument parser for use with this program.
651 Message toolDescription = INFO_LDAPMODIFY_TOOL_DESCRIPTION.get();
652 ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
653 false);
654 try
655 {
656 propertiesFileArgument = new StringArgument("propertiesFilePath",
657 null, OPTION_LONG_PROP_FILE_PATH,
658 false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
659 INFO_DESCRIPTION_PROP_FILE_PATH.get());
660 argParser.addArgument(propertiesFileArgument);
661 argParser.setFilePropertiesArgument(propertiesFileArgument);
662
663 noPropertiesFileArgument = new BooleanArgument(
664 "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
665 INFO_DESCRIPTION_NO_PROP_FILE.get());
666 argParser.addArgument(noPropertiesFileArgument);
667 argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
668
669 hostName = new StringArgument("host", OPTION_SHORT_HOST,
670 OPTION_LONG_HOST, false, false, true,
671 INFO_HOST_PLACEHOLDER.get(), "localhost",
672 null,
673 INFO_DESCRIPTION_HOST.get());
674 hostName.setPropertyName(OPTION_LONG_HOST);
675 argParser.addArgument(hostName);
676
677 port = new IntegerArgument("port", OPTION_SHORT_PORT,
678 OPTION_LONG_PORT, false, false, true,
679 INFO_PORT_PLACEHOLDER.get(), 389, null,
680 INFO_DESCRIPTION_PORT.get());
681 port.setPropertyName(OPTION_LONG_PORT);
682 argParser.addArgument(port);
683
684 useSSL = new BooleanArgument("useSSL", OPTION_SHORT_USE_SSL,
685 OPTION_LONG_USE_SSL,
686 INFO_DESCRIPTION_USE_SSL.get());
687 useSSL.setPropertyName(OPTION_LONG_USE_SSL);
688 argParser.addArgument(useSSL);
689
690 startTLS = new BooleanArgument("startTLS", OPTION_SHORT_START_TLS,
691 OPTION_LONG_START_TLS,
692 INFO_DESCRIPTION_START_TLS.get());
693 startTLS.setPropertyName(OPTION_LONG_START_TLS);
694 argParser.addArgument(startTLS);
695
696 bindDN = new StringArgument("bindDN", OPTION_SHORT_BINDDN,
697 OPTION_LONG_BINDDN, false, false, true,
698 INFO_BINDDN_PLACEHOLDER.get(), null, null,
699 INFO_DESCRIPTION_BINDDN.get());
700 bindDN.setPropertyName(OPTION_LONG_BINDDN);
701 argParser.addArgument(bindDN);
702
703 bindPassword = new StringArgument("bindPassword", OPTION_SHORT_BINDPWD,
704 OPTION_LONG_BINDPWD,
705 false, false, true,
706 INFO_BINDPWD_PLACEHOLDER.get(),
707 null, null,
708 INFO_DESCRIPTION_BINDPASSWORD.get());
709 bindPassword.setPropertyName(OPTION_LONG_BINDPWD);
710 argParser.addArgument(bindPassword);
711
712 bindPasswordFile =
713 new FileBasedArgument("bindPasswordFile",
714 OPTION_SHORT_BINDPWD_FILE,
715 OPTION_LONG_BINDPWD_FILE,
716 false, false,
717 INFO_BINDPWD_FILE_PLACEHOLDER.get(), null,
718 null, INFO_DESCRIPTION_BINDPASSWORDFILE.get());
719 bindPasswordFile.setPropertyName(OPTION_LONG_BINDPWD_FILE);
720 argParser.addArgument(bindPasswordFile);
721
722 defaultAdd = new BooleanArgument(
723 "defaultAdd", 'a', "defaultAdd",
724 INFO_MODIFY_DESCRIPTION_DEFAULT_ADD.get());
725 argParser.addArgument(defaultAdd);
726
727 filename = new StringArgument("filename", OPTION_SHORT_FILENAME,
728 OPTION_LONG_FILENAME, false, false,
729 true, INFO_FILE_PLACEHOLDER.get(), null,
730 null,
731 INFO_LDAPMODIFY_DESCRIPTION_FILENAME.get());
732 filename.setPropertyName(OPTION_LONG_FILENAME);
733 argParser.addArgument(filename);
734
735 saslExternal = new BooleanArgument(
736 "useSASLExternal", 'r',
737 "useSASLExternal",
738 INFO_DESCRIPTION_USE_SASL_EXTERNAL.get());
739 saslExternal.setPropertyName("useSASLExternal");
740 argParser.addArgument(saslExternal);
741
742 saslOptions = new StringArgument("saslOption", OPTION_SHORT_SASLOPTION,
743 OPTION_LONG_SASLOPTION, false,
744 true, true,
745 INFO_SASL_OPTION_PLACEHOLDER.get(), null,
746 null,
747 INFO_DESCRIPTION_SASL_PROPERTIES.get());
748 saslOptions.setPropertyName(OPTION_LONG_SASLOPTION);
749 argParser.addArgument(saslOptions);
750
751 trustAll = new BooleanArgument("trustAll", 'X', "trustAll",
752 INFO_DESCRIPTION_TRUSTALL.get());
753 trustAll.setPropertyName("trustAll");
754 argParser.addArgument(trustAll);
755
756 keyStorePath = new StringArgument("keyStorePath",
757 OPTION_SHORT_KEYSTOREPATH,
758 OPTION_LONG_KEYSTOREPATH,
759 false, false, true,
760 INFO_KEYSTOREPATH_PLACEHOLDER.get(),
761 null, null,
762 INFO_DESCRIPTION_KEYSTOREPATH.get());
763 keyStorePath.setPropertyName(OPTION_LONG_KEYSTOREPATH);
764 argParser.addArgument(keyStorePath);
765
766 keyStorePassword =
767 new StringArgument("keyStorePassword",
768 OPTION_SHORT_KEYSTORE_PWD,
769 OPTION_LONG_KEYSTORE_PWD,
770 false, false,
771 true,
772 INFO_KEYSTORE_PWD_PLACEHOLDER.get(),
773 null, null,
774 INFO_DESCRIPTION_KEYSTOREPASSWORD.get());
775 keyStorePassword.setPropertyName(OPTION_LONG_KEYSTORE_PWD);
776 argParser.addArgument(keyStorePassword);
777
778 keyStorePasswordFile =
779 new FileBasedArgument("keystorepasswordfile",
780 OPTION_SHORT_KEYSTORE_PWD_FILE,
781 OPTION_LONG_KEYSTORE_PWD_FILE,
782 false, false,
783 INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get(),
784 null, null,
785 INFO_DESCRIPTION_KEYSTOREPASSWORD_FILE.get());
786 keyStorePasswordFile.setPropertyName(OPTION_LONG_KEYSTORE_PWD_FILE);
787 argParser.addArgument(keyStorePasswordFile);
788
789 certNickname = new StringArgument(
790 "certnickname", 'N', "certNickname",
791 false, false, true, INFO_NICKNAME_PLACEHOLDER.get(), null,
792 null, INFO_DESCRIPTION_CERT_NICKNAME.get());
793 certNickname.setPropertyName("certNickname");
794 argParser.addArgument(certNickname);
795
796 trustStorePath = new StringArgument(
797 "trustStorePath",
798 OPTION_SHORT_TRUSTSTOREPATH,
799 OPTION_LONG_TRUSTSTOREPATH,
800 false, false, true,
801 INFO_TRUSTSTOREPATH_PLACEHOLDER.get(),
802 null, null,
803 INFO_DESCRIPTION_TRUSTSTOREPATH.get());
804 trustStorePath.setPropertyName(OPTION_LONG_TRUSTSTOREPATH);
805 argParser.addArgument(trustStorePath);
806
807 trustStorePassword =
808 new StringArgument("trustStorePassword", null,
809 OPTION_LONG_TRUSTSTORE_PWD ,
810 false, false, true,
811 INFO_TRUSTSTORE_PWD_PLACEHOLDER.get(), null,
812 null, INFO_DESCRIPTION_TRUSTSTOREPASSWORD.get());
813 trustStorePassword.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD);
814 argParser.addArgument(trustStorePassword);
815
816 trustStorePasswordFile =
817 new FileBasedArgument(
818 "truststorepasswordfile",
819 OPTION_SHORT_TRUSTSTORE_PWD_FILE,
820 OPTION_LONG_TRUSTSTORE_PWD_FILE, false, false,
821 INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER.get(), null, null,
822 INFO_DESCRIPTION_TRUSTSTOREPASSWORD_FILE.get());
823 trustStorePasswordFile.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD_FILE);
824 argParser.addArgument(trustStorePasswordFile);
825
826 proxyAuthzID = new StringArgument("proxy_authzid",
827 OPTION_SHORT_PROXYAUTHID,
828 OPTION_LONG_PROXYAUTHID, false,
829 false, true,
830 INFO_PROXYAUTHID_PLACEHOLDER.get(),
831 null, null,
832 INFO_DESCRIPTION_PROXY_AUTHZID.get());
833 proxyAuthzID.setPropertyName(OPTION_LONG_PROXYAUTHID);
834 argParser.addArgument(proxyAuthzID);
835
836 reportAuthzID = new BooleanArgument(
837 "reportauthzid", 'E',
838 "reportAuthzID",
839 INFO_DESCRIPTION_REPORT_AUTHZID.get());
840 reportAuthzID.setPropertyName("reportAuthzID");
841 argParser.addArgument(reportAuthzID);
842
843 assertionFilter = new StringArgument(
844 "assertionfilter", null,
845 OPTION_LONG_ASSERTION_FILE,
846 false, false,
847 true,
848 INFO_ASSERTION_FILTER_PLACEHOLDER.get(),
849 null, null,
850 INFO_DESCRIPTION_ASSERTION_FILTER.get());
851 assertionFilter.setPropertyName(OPTION_LONG_ASSERTION_FILE);
852 argParser.addArgument(assertionFilter);
853
854 preReadAttributes = new StringArgument(
855 "prereadattrs", null,
856 "preReadAttributes", false, false,
857 true, INFO_ATTRIBUTE_LIST_PLACEHOLDER.get(), null, null,
858 INFO_DESCRIPTION_PREREAD_ATTRS.get());
859 preReadAttributes.setPropertyName("preReadAttributes");
860 argParser.addArgument(preReadAttributes);
861
862 postReadAttributes = new StringArgument(
863 "postreadattrs", null,
864 "postReadAttributes", false,
865 false, true, INFO_ATTRIBUTE_LIST_PLACEHOLDER.get(), null,
866 null,
867 INFO_DESCRIPTION_POSTREAD_ATTRS.get());
868 postReadAttributes.setPropertyName("postReadAttributes");
869 argParser.addArgument(postReadAttributes);
870
871 controlStr =
872 new StringArgument("control", 'J', "control", false, true, true,
873 INFO_LDAP_CONTROL_PLACEHOLDER.get(),
874 null, null, INFO_DESCRIPTION_CONTROLS.get());
875 controlStr.setPropertyName("control");
876 argParser.addArgument(controlStr);
877
878 version = new IntegerArgument("version", OPTION_SHORT_PROTOCOL_VERSION,
879 OPTION_LONG_PROTOCOL_VERSION,
880 false, false, true,
881 INFO_PROTOCOL_VERSION_PLACEHOLDER.get(), 3, null,
882 INFO_DESCRIPTION_VERSION.get());
883 version.setPropertyName(OPTION_LONG_PROTOCOL_VERSION);
884 argParser.addArgument(version);
885
886 encodingStr = new StringArgument("encoding", 'i', "encoding",
887 false, false,
888 true, INFO_ENCODING_PLACEHOLDER.get(),
889 null, null,
890 INFO_DESCRIPTION_ENCODING.get());
891 encodingStr.setPropertyName("encoding");
892 argParser.addArgument(encodingStr);
893
894 continueOnError = new BooleanArgument("continueOnError", 'c',
895 "continueOnError",
896 INFO_DESCRIPTION_CONTINUE_ON_ERROR.get());
897 continueOnError.setPropertyName("continueOnError");
898 argParser.addArgument(continueOnError);
899
900 noop = new BooleanArgument("no-op", OPTION_SHORT_DRYRUN,
901 OPTION_LONG_DRYRUN,
902 INFO_DESCRIPTION_NOOP.get());
903 noop.setPropertyName(OPTION_LONG_DRYRUN);
904 argParser.addArgument(noop);
905
906 verbose = new BooleanArgument("verbose", 'v', "verbose",
907 INFO_DESCRIPTION_VERBOSE.get());
908 verbose.setPropertyName("verbose");
909 argParser.addArgument(verbose);
910
911 showUsage = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
912 OPTION_LONG_HELP,
913 INFO_DESCRIPTION_SHOWUSAGE.get());
914 argParser.addArgument(showUsage);
915 argParser.setUsageArgument(showUsage, out);
916 } catch (ArgumentException ae)
917 {
918 Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
919
920 err.println(wrapText(message, MAX_LINE_WIDTH));
921 return 1;
922 }
923
924 // Parse the command-line arguments provided to this program.
925 try
926 {
927 argParser.parseArguments(args);
928 }
929 catch (ArgumentException ae)
930 {
931 Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
932
933 err.println(wrapText(message, MAX_LINE_WIDTH));
934 err.println(argParser.getUsage());
935 return 1;
936 }
937
938 // If we should just display usage or version information,
939 // then print it and exit.
940 if (argParser.usageOrVersionDisplayed())
941 {
942 return 0;
943 }
944
945 if(bindPassword.isPresent() && bindPasswordFile.isPresent())
946 {
947 Message message = ERR_TOOL_CONFLICTING_ARGS.get(
948 bindPassword.getLongIdentifier(),
949 bindPasswordFile.getLongIdentifier());
950 err.println(wrapText(message, MAX_LINE_WIDTH));
951 return 1;
952 }
953
954 String hostNameValue = hostName.getValue();
955 int portNumber = 389;
956 try
957 {
958 portNumber = port.getIntValue();
959 } catch(ArgumentException ae)
960 {
961 if (debugEnabled())
962 {
963 TRACER.debugCaught(DebugLogLevel.ERROR, ae);
964 }
965 err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
966 return 1;
967 }
968
969 try
970 {
971 int versionNumber = version.getIntValue();
972 if(versionNumber != 2 && versionNumber != 3)
973 {
974
975 err.println(wrapText(ERR_DESCRIPTION_INVALID_VERSION.get(
976 String.valueOf(versionNumber)), MAX_LINE_WIDTH));
977 return 1;
978 }
979 connectionOptions.setVersionNumber(versionNumber);
980 } catch(ArgumentException ae)
981 {
982 if (debugEnabled())
983 {
984 TRACER.debugCaught(DebugLogLevel.ERROR, ae);
985 }
986 err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
987 return 1;
988 }
989
990 String bindDNValue = bindDN.getValue();
991 String fileNameValue = filename.getValue();
992 String bindPasswordValue = bindPassword.getValue();
993 if(bindPasswordValue != null && bindPasswordValue.equals("-"))
994 {
995 // read the password from the stdin.
996 try
997 {
998 out.print(INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDNValue));
999 char[] pwChars = PasswordReader.readPassword();
1000 bindPasswordValue = new String(pwChars);
1001 } catch(Exception ex)
1002 {
1003 if (debugEnabled())
1004 {
1005 TRACER.debugCaught(DebugLogLevel.ERROR, ex);
1006 }
1007 err.println(wrapText(ex.getMessage(), MAX_LINE_WIDTH));
1008 return 1;
1009 }
1010 } else if(bindPasswordValue == null)
1011 {
1012 // Read from file if it exists.
1013 bindPasswordValue = bindPasswordFile.getValue();
1014 }
1015
1016 String keyStorePathValue = keyStorePath.getValue();
1017 String trustStorePathValue = trustStorePath.getValue();
1018
1019 String keyStorePasswordValue = null;
1020 if (keyStorePassword.isPresent())
1021 {
1022 keyStorePasswordValue = keyStorePassword.getValue();
1023 }
1024 else if (keyStorePasswordFile.isPresent())
1025 {
1026 keyStorePasswordValue = keyStorePasswordFile.getValue();
1027 }
1028
1029 String trustStorePasswordValue = null;
1030 if (trustStorePassword.isPresent())
1031 {
1032 trustStorePasswordValue = trustStorePassword.getValue();
1033 }
1034 else if (trustStorePasswordFile.isPresent())
1035 {
1036 trustStorePasswordValue = trustStorePasswordFile.getValue();
1037 }
1038
1039 modifyOptions.setShowOperations(noop.isPresent());
1040 modifyOptions.setVerbose(verbose.isPresent());
1041 modifyOptions.setContinueOnError(continueOnError.isPresent());
1042 modifyOptions.setEncoding(encodingStr.getValue());
1043 modifyOptions.setDefaultAdd(defaultAdd.isPresent());
1044
1045 if (controlStr.isPresent())
1046 {
1047 for (String ctrlString : controlStr.getValues())
1048 {
1049 LDAPControl ctrl = LDAPToolUtils.getControl(ctrlString, err);
1050 if(ctrl == null)
1051 {
1052 Message message = ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString);
1053 err.println(wrapText(message, MAX_LINE_WIDTH));
1054 err.println(argParser.getUsage());
1055 return 1;
1056 }
1057 modifyOptions.getControls().add(ctrl);
1058 }
1059 }
1060
1061 if (proxyAuthzID.isPresent())
1062 {
1063 ASN1OctetString proxyValue = new ASN1OctetString(proxyAuthzID.getValue());
1064
1065 LDAPControl proxyControl =
1066 new LDAPControl(OID_PROXIED_AUTH_V2, true, proxyValue);
1067 modifyOptions.getControls().add(proxyControl);
1068 }
1069
1070 if (assertionFilter.isPresent())
1071 {
1072 String filterString = assertionFilter.getValue();
1073 LDAPFilter filter;
1074 try
1075 {
1076 filter = LDAPFilter.decode(filterString);
1077
1078 LDAPControl assertionControl =
1079 new LDAPControl(OID_LDAP_ASSERTION, true,
1080 new ASN1OctetString(filter.encode().encode()));
1081 modifyOptions.getControls().add(assertionControl);
1082 }
1083 catch (LDAPException le)
1084 {
1085 Message message = ERR_LDAP_ASSERTION_INVALID_FILTER.get(
1086 le.getMessage());
1087 err.println(wrapText(message, MAX_LINE_WIDTH));
1088 return 1;
1089 }
1090 }
1091
1092 if (preReadAttributes.isPresent())
1093 {
1094 String valueStr = preReadAttributes.getValue();
1095 ArrayList<ASN1Element> attrElements = new ArrayList<ASN1Element>();
1096
1097 StringTokenizer tokenizer = new StringTokenizer(valueStr, ", ");
1098 while (tokenizer.hasMoreTokens())
1099 {
1100 attrElements.add(new ASN1OctetString(tokenizer.nextToken()));
1101 }
1102
1103 ASN1OctetString controlValue =
1104 new ASN1OctetString(new ASN1Sequence(attrElements).encode());
1105 LDAPControl c = new LDAPControl(OID_LDAP_READENTRY_PREREAD, true,
1106 controlValue);
1107 modifyOptions.getControls().add(c);
1108 }
1109
1110 if (postReadAttributes.isPresent())
1111 {
1112 String valueStr = postReadAttributes.getValue();
1113 ArrayList<ASN1Element> attrElements = new ArrayList<ASN1Element>();
1114
1115 StringTokenizer tokenizer = new StringTokenizer(valueStr, ", ");
1116 while (tokenizer.hasMoreTokens())
1117 {
1118 attrElements.add(new ASN1OctetString(tokenizer.nextToken()));
1119 }
1120
1121 ASN1OctetString controlValue =
1122 new ASN1OctetString(new ASN1Sequence(attrElements).encode());
1123 LDAPControl c = new LDAPControl(OID_LDAP_READENTRY_POSTREAD, true,
1124 controlValue);
1125 modifyOptions.getControls().add(c);
1126 }
1127
1128 // Set the connection options.
1129 connectionOptions.setSASLExternal(saslExternal.isPresent());
1130 if(saslOptions.isPresent())
1131 {
1132 LinkedList<String> values = saslOptions.getValues();
1133 for(String saslOption : values)
1134 {
1135 if(saslOption.startsWith("mech="))
1136 {
1137 boolean val = connectionOptions.setSASLMechanism(saslOption);
1138 if(val == false)
1139 {
1140 return 1;
1141 }
1142 } else
1143 {
1144 boolean val = connectionOptions.addSASLProperty(saslOption);
1145 if(val == false)
1146 {
1147 return 1;
1148 }
1149 }
1150 }
1151 }
1152
1153 connectionOptions.setUseSSL(useSSL.isPresent());
1154 connectionOptions.setStartTLS(startTLS.isPresent());
1155 connectionOptions.setReportAuthzID(reportAuthzID.isPresent());
1156
1157 if(connectionOptions.useSASLExternal())
1158 {
1159 if(!connectionOptions.useSSL() && !connectionOptions.useStartTLS())
1160 {
1161 Message message = ERR_TOOL_SASLEXTERNAL_NEEDS_SSL_OR_TLS.get();
1162 err.println(wrapText(message, MAX_LINE_WIDTH));
1163 return 1;
1164 }
1165 if(keyStorePathValue == null)
1166 {
1167 Message message = ERR_TOOL_SASLEXTERNAL_NEEDS_KEYSTORE.get();
1168 err.println(wrapText(message, MAX_LINE_WIDTH));
1169 return 1;
1170 }
1171 }
1172
1173 connectionOptions.setVerbose(verbose.isPresent());
1174
1175 LDAPModify ldapModify = null;
1176 try
1177 {
1178 if (initializeServer)
1179 {
1180 // Bootstrap and initialize directory data structures.
1181 EmbeddedUtils.initializeForClientUse();
1182 }
1183
1184 // Connect to the specified host with the supplied userDN and password.
1185 SSLConnectionFactory sslConnectionFactory = null;
1186 if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
1187 {
1188 String clientAlias;
1189 if (certNickname.isPresent())
1190 {
1191 clientAlias = certNickname.getValue();
1192 }
1193 else
1194 {
1195 clientAlias = null;
1196 }
1197
1198 sslConnectionFactory = new SSLConnectionFactory();
1199 sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
1200 keyStorePasswordValue, clientAlias,
1201 trustStorePathValue, trustStorePasswordValue);
1202 connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
1203 }
1204
1205 AtomicInteger nextMessageID = new AtomicInteger(1);
1206 connection = new LDAPConnection(hostNameValue, portNumber,
1207 connectionOptions, out, err);
1208 connection.connectToHost(bindDNValue, bindPasswordValue, nextMessageID);
1209
1210 ldapModify = new LDAPModify(fileNameValue, nextMessageID, out, err);
1211 ldapModify.readAndExecute(connection, fileNameValue, modifyOptions);
1212 } catch(LDAPException le)
1213 {
1214 if (debugEnabled())
1215 {
1216 TRACER.debugCaught(DebugLogLevel.ERROR, le);
1217 }
1218 LDAPToolUtils.printErrorMessage(err, le.getMessageObject(),
1219 le.getResultCode(),
1220 le.getErrorMessage(), le.getMatchedDN());
1221 int code = le.getResultCode();
1222 return code;
1223 } catch(LDAPConnectionException lce)
1224 {
1225 if (debugEnabled())
1226 {
1227 TRACER.debugCaught(DebugLogLevel.ERROR, lce);
1228 }
1229 LDAPToolUtils.printErrorMessage(err, lce.getMessageObject(),
1230 lce.getResultCode(),
1231 lce.getErrorMessage(),
1232 lce.getMatchedDN());
1233 int code = lce.getResultCode();
1234 return code;
1235 } catch(Exception e)
1236 {
1237 if (debugEnabled())
1238 {
1239 TRACER.debugCaught(DebugLogLevel.ERROR, e);
1240 }
1241 err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
1242 return 1;
1243 } finally
1244 {
1245 if(connection != null)
1246 {
1247 if (ldapModify == null)
1248 {
1249 connection.close(null);
1250 }
1251 else
1252 {
1253 connection.close(ldapModify.nextMessageID);
1254 }
1255 }
1256 }
1257 return 0;
1258 }
1259
1260 }
1261