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