/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. */ package org.opends.server.tools; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.asn1.ASN1Element; import org.opends.server.protocols.asn1.ASN1Exception; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.protocols.asn1.ASN1Sequence; import org.opends.server.protocols.ldap.AddRequestProtocolOp; import org.opends.server.protocols.ldap.AddResponseProtocolOp; import org.opends.server.protocols.ldap.DeleteRequestProtocolOp; import org.opends.server.protocols.ldap.DeleteResponseProtocolOp; import org.opends.server.protocols.ldap.LDAPAttribute; import org.opends.server.protocols.ldap.LDAPControl; import org.opends.server.protocols.ldap.LDAPException; import org.opends.server.protocols.ldap.LDAPFilter; import org.opends.server.protocols.ldap.LDAPMessage; import org.opends.server.protocols.ldap.ModifyRequestProtocolOp; import org.opends.server.protocols.ldap.ModifyResponseProtocolOp; import org.opends.server.protocols.ldap.ModifyDNRequestProtocolOp; import org.opends.server.protocols.ldap.ModifyDNResponseProtocolOp; import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp; import org.opends.server.protocols.ldap.ProtocolOp; import org.opends.server.types.Attribute; import org.opends.server.types.LDIFImportConfig; import org.opends.server.util.AddChangeRecordEntry; import org.opends.server.util.ChangeRecordEntry; import org.opends.server.util.LDIFException; import org.opends.server.util.LDIFReader; import org.opends.server.util.ModifyChangeRecordEntry; import org.opends.server.util.ModifyDNChangeRecordEntry; import org.opends.server.util.PasswordReader; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.ArgumentParser; import org.opends.server.util.args.BooleanArgument; import org.opends.server.util.args.FileBasedArgument; import org.opends.server.util.args.IntegerArgument; import org.opends.server.util.args.StringArgument; import static org.opends.server.loggers.Debug.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.protocols.ldap.LDAPResultCode.*; import static org.opends.server.util.ServerConstants.*; /** * This class provides a tool that can be used to issue modify requests to the * Directory Server. */ public class LDAPModify { /** * The fully-qualified name of this class for debugging purposes. */ private static final String CLASS_NAME = "org.opends.server.tools.LDAPModify"; // The LDIF file name. private String fileName = null; /** * Constructor for the LDAPModify object. * * @param fileName The name of the file containing the LDIF data to use for * the modifications. */ public LDAPModify(String fileName) { if(fileName == null) { this.fileName = "Console"; } else { this.fileName = fileName; } } /** * Read the specified change records from the given input stream * (file or stdin) and execute the given modify request. * * @param connection The connection to use for this modify request. * @param is The input stream to read the change records from. * @param modifyOptions The constraints for the modify request. * * @throws IOException If a problem occurs while attempting to communicate * with the Directory Server. * * @throws LDAPException If the Directory Server returns an error response. */ public void readAndExecute(LDAPConnection connection, InputStream is, LDAPModifyOptions modifyOptions) throws IOException, LDAPException { ArrayList controls = modifyOptions.getControls(); LDIFReader reader; // Create an LDIF import configuration to do this and then get the reader. try { LDIFImportConfig importConfig = new LDIFImportConfig(is); reader = new LDIFReader(importConfig); } catch (Exception e) { assert debugException(CLASS_NAME, "readAndExecute", e); int msgID = MSGID_LDIF_FILE_CANNOT_OPEN_FOR_READ; String message = getMessage(msgID, is, String.valueOf(e)); throw new IOException(message); } int messageID = 1; while (true) { messageID++; ChangeRecordEntry entry = null; try { entry = reader.readChangeRecord(modifyOptions.getDefaultAdd()); } catch (LDIFException le) { assert debugException(CLASS_NAME, "readAndExecute", le); if(!modifyOptions.continueOnError()) { try { reader.close(); } catch (Exception e) { assert debugException(CLASS_NAME, "readAndExecute", e); } int msgID = MSGID_LDIF_FILE_INVALID_LDIF_ENTRY; String message = getMessage(msgID, le.getLineNumber(), fileName, String.valueOf(le)); throw new IOException(message); } else { int msgID = MSGID_LDIF_FILE_INVALID_LDIF_ENTRY; String message = getMessage(msgID, le.getLineNumber(), fileName, String.valueOf(le)); System.err.println(message); continue; } } catch (Exception e) { assert debugException(CLASS_NAME, "readAndExecute", e); if(!modifyOptions.continueOnError()) { try { reader.close(); } catch (Exception e2) { assert debugException(CLASS_NAME, "readAndExecute", e2); } int msgID = MSGID_LDIF_FILE_READ_ERROR; String message = getMessage(msgID, fileName, String.valueOf(e)); throw new IOException(message); } else { int msgID = MSGID_LDIF_FILE_READ_ERROR; String message = getMessage(msgID, fileName, String.valueOf(e)); System.err.println(message); continue; } } // If the entry is null, then we have reached the end of the config file. if(entry == null) { try { reader.close(); } catch (Exception e) { assert debugException(CLASS_NAME, "readAndExecute", e); } break; } ProtocolOp protocolOp = null; ASN1OctetString asn1OctetStr = new ASN1OctetString(entry.getDN().toString()); String operationType = ""; int msgID = 0; switch(entry.getChangeOperationType()) { case ADD: operationType = "ADD"; AddChangeRecordEntry addEntry = (AddChangeRecordEntry) entry; ArrayList attrs = addEntry.getAttributes(); ArrayList attributes = new ArrayList(attrs.size()); for(Attribute a : attrs) { attributes.add(new LDAPAttribute(a)); } protocolOp = new AddRequestProtocolOp(asn1OctetStr, attributes); msgID = MSGID_PROCESSING_OPERATION; System.out.println(getMessage(msgID, operationType, asn1OctetStr)); break; case DELETE: operationType = "DELETE"; protocolOp = new DeleteRequestProtocolOp(asn1OctetStr); msgID = MSGID_PROCESSING_OPERATION; System.out.println(getMessage(msgID, operationType, asn1OctetStr)); break; case MODIFY: operationType = "MODIFY"; ModifyChangeRecordEntry modEntry = (ModifyChangeRecordEntry) entry; protocolOp = new ModifyRequestProtocolOp(asn1OctetStr, modEntry.getModifications()); msgID = MSGID_PROCESSING_OPERATION; System.out.println(getMessage(msgID, operationType, asn1OctetStr)); break; case MODIFY_DN: operationType = "MODIFY DN"; ModifyDNChangeRecordEntry modDNEntry = (ModifyDNChangeRecordEntry) entry; if(modDNEntry.getNewSuperiorRDN() != null) { protocolOp = new ModifyDNRequestProtocolOp(asn1OctetStr, new ASN1OctetString(modDNEntry.getNewRDN().toString()), modDNEntry.deleteOldRDN(), new ASN1OctetString( modDNEntry.getNewSuperiorRDN().toString())); } else { protocolOp = new ModifyDNRequestProtocolOp(asn1OctetStr, new ASN1OctetString(modDNEntry.getNewRDN().toString()), modDNEntry.deleteOldRDN()); } msgID = MSGID_PROCESSING_OPERATION; System.out.println(getMessage(msgID, operationType, asn1OctetStr)); break; default: break; } if(!modifyOptions.showOperations()) { LDAPMessage responseMessage = null; try { LDAPMessage message = new LDAPMessage(messageID, protocolOp, controls); // int numBytes = connection.getASN1Writer().writeElement(message.encode()); ASN1Element element = connection.getASN1Reader().readElement(); responseMessage = LDAPMessage.decode(ASN1Sequence.decodeAsSequence(element)); } catch(ASN1Exception ae) { assert debugException(CLASS_NAME, "readAndExecute", ae); msgID = MSGID_OPERATION_FAILED; System.err.println(getMessage(msgID, operationType, asn1OctetStr, ae.getMessage())); if(!modifyOptions.continueOnError()) { throw new IOException(ae.getMessage()); } return; } int resultCode = 0; String errorMessage = null; List referralURLs = null; switch(entry.getChangeOperationType()) { case ADD: AddResponseProtocolOp addOp = responseMessage.getAddResponseProtocolOp(); resultCode = addOp.getResultCode(); errorMessage = addOp.getErrorMessage(); referralURLs = addOp.getReferralURLs(); break; case DELETE: DeleteResponseProtocolOp delOp = responseMessage.getDeleteResponseProtocolOp(); resultCode = delOp.getResultCode(); errorMessage = delOp.getErrorMessage(); referralURLs = delOp.getReferralURLs(); break; case MODIFY: ModifyResponseProtocolOp modOp = responseMessage.getModifyResponseProtocolOp(); resultCode = modOp.getResultCode(); errorMessage = modOp.getErrorMessage(); referralURLs = modOp.getReferralURLs(); break; case MODIFY_DN: ModifyDNResponseProtocolOp modDNOp = responseMessage.getModifyDNResponseProtocolOp(); resultCode = modDNOp.getResultCode(); errorMessage = modDNOp.getErrorMessage(); referralURLs = modDNOp.getReferralURLs(); break; default: break; } if(resultCode != SUCCESS && resultCode != REFERRAL) { if(errorMessage == null) { errorMessage = "Result code:" + resultCode; } msgID = MSGID_OPERATION_FAILED; String msg = getMessage(msgID, operationType, asn1OctetStr, errorMessage); if(!modifyOptions.continueOnError()) { throw new LDAPException(resultCode, msgID, msg); } else { System.err.println(msg); } } else { msgID = MSGID_OPERATION_SUCCESSFUL; String msg = getMessage(msgID, operationType, asn1OctetStr); System.out.println(msg); if (errorMessage != null) { System.out.println(errorMessage); } if (referralURLs != null) { System.out.println(referralURLs); } } for (LDAPControl c : responseMessage.getControls()) { String oid = c.getOID(); if (oid.equals(OID_LDAP_READENTRY_PREREAD)) { ASN1OctetString controlValue = c.getValue(); if (controlValue == null) { msgID = MSGID_LDAPMODIFY_PREREAD_NO_VALUE; System.err.println(getMessage(msgID)); continue; } SearchResultEntryProtocolOp searchEntry; try { byte[] valueBytes = controlValue.value(); ASN1Element valueElement = ASN1Element.decode(valueBytes); searchEntry = SearchResultEntryProtocolOp.decodeSearchEntry(valueElement); } catch (ASN1Exception ae) { msgID = MSGID_LDAPMODIFY_PREREAD_CANNOT_DECODE_VALUE; System.err.println(getMessage(msgID, ae.getMessage())); continue; } catch (LDAPException le) { msgID = MSGID_LDAPMODIFY_PREREAD_CANNOT_DECODE_VALUE; System.err.println(getMessage(msgID, le.getMessage())); continue; } StringBuilder buffer = new StringBuilder(); searchEntry.toLDIF(buffer, 78); System.out.println(getMessage(MSGID_LDAPMODIFY_PREREAD_ENTRY)); System.out.println(buffer); } else if (oid.equals(OID_LDAP_READENTRY_POSTREAD)) { ASN1OctetString controlValue = c.getValue(); if (controlValue == null) { msgID = MSGID_LDAPMODIFY_POSTREAD_NO_VALUE; System.err.println(getMessage(msgID)); continue; } SearchResultEntryProtocolOp searchEntry; try { byte[] valueBytes = controlValue.value(); ASN1Element valueElement = ASN1Element.decode(valueBytes); searchEntry = SearchResultEntryProtocolOp.decodeSearchEntry(valueElement); } catch (ASN1Exception ae) { msgID = MSGID_LDAPMODIFY_POSTREAD_CANNOT_DECODE_VALUE; System.err.println(getMessage(msgID, ae.getMessage())); continue; } catch (LDAPException le) { msgID = MSGID_LDAPMODIFY_POSTREAD_CANNOT_DECODE_VALUE; System.err.println(getMessage(msgID, le.getMessage())); continue; } StringBuilder buffer = new StringBuilder(); searchEntry.toLDIF(buffer, 78); System.out.println(getMessage(MSGID_LDAPMODIFY_POSTREAD_ENTRY)); System.out.println(buffer); } } } } } /** * The main method for LDAPModify tool. * * @param args The command-line arguments provided to this program. */ public static void main(String[] args) { int retCode = mainModify(args); if(retCode != 0) { System.exit(retCode); } } /** * Parses the provided command-line arguments and uses that information to * run the ldapmodify tool. * * @param args The command-line arguments provided to this program. * * @return The error code. */ public static int mainModify(String[] args) { LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions(); LDAPModifyOptions modifyOptions = new LDAPModifyOptions(); LDAPConnection connection = null; BooleanArgument trustAll = null; StringArgument assertionFilter = null; StringArgument bindDN = null; StringArgument bindPassword = null; FileBasedArgument bindPasswordFile = null; StringArgument proxyAuthzID = null; BooleanArgument reportAuthzID = null; StringArgument encodingStr = null; StringArgument keyStorePath = null; StringArgument keyStorePassword = null; StringArgument trustStorePath = null; // StringArgument trustStorePassword = null; StringArgument hostName = null; IntegerArgument port = null; BooleanArgument showUsage = null; StringArgument controlStr = null; BooleanArgument verbose = null; BooleanArgument continueOnError = null; BooleanArgument useSSL = null; BooleanArgument startTLS = null; BooleanArgument saslExternal = null; BooleanArgument defaultAdd = null; StringArgument filename = null; StringArgument saslOptions = null; StringArgument preReadAttributes = null; StringArgument postReadAttributes = null; IntegerArgument version = null; BooleanArgument noop = null; // Create the command-line argument parser for use with this program. ArgumentParser argParser = new ArgumentParser(CLASS_NAME, false); try { trustAll = new BooleanArgument("trustAll", 'X', "trustAll", MSGID_DESCRIPTION_TRUSTALL); argParser.addArgument(trustAll); bindDN = new StringArgument("bindDN", 'D', "bindDN", false, false, true, "{bindDN}", null, null, MSGID_DESCRIPTION_BINDDN); argParser.addArgument(bindDN); bindPassword = new StringArgument("bindPassword", 'w', "bindPassword", false, false, true, "{bindPassword}", null, null, MSGID_DESCRIPTION_BINDPASSWORD); argParser.addArgument(bindPassword); bindPasswordFile = new FileBasedArgument("bindPasswordFile", 'j', "bindPasswordFile", false, false, "{bindPasswordFilename}", null, null, MSGID_DESCRIPTION_BINDPASSWORDFILE); argParser.addArgument(bindPasswordFile); proxyAuthzID = new StringArgument("proxy_authzid", 'Y', "proxyAs", false, false, true, "{authzID}", null, null, MSGID_DESCRIPTION_PROXY_AUTHZID); argParser.addArgument(proxyAuthzID); reportAuthzID = new BooleanArgument("reportauthzid", 'E', "reportAuthzID", MSGID_DESCRIPTION_REPORT_AUTHZID); argParser.addArgument(reportAuthzID); encodingStr = new StringArgument("encoding", 'i', "encoding", false, false, true, "{encoding}", null, null, MSGID_DESCRIPTION_ENCODING); argParser.addArgument(encodingStr); keyStorePath = new StringArgument("keyStorePath", 'K', "keyStorePath", false, false, true, "{keyStorePath}", null, null, MSGID_DESCRIPTION_KEYSTOREPATH); argParser.addArgument(keyStorePath); trustStorePath = new StringArgument("trustStorePath", 'P', "trustStorePath", false, false, true, "{trustStorePath}", null, null, MSGID_DESCRIPTION_TRUSTSTOREPATH); argParser.addArgument(trustStorePath); keyStorePassword = new StringArgument("keyStorePassword", 'W', "keyStorePassword", false, false, true, "{keyStorePassword}", null, null, MSGID_DESCRIPTION_KEYSTOREPASSWORD); argParser.addArgument(keyStorePassword); hostName = new StringArgument("host", 'h', "host", false, false, true, "{host}", "localhost", null, MSGID_DESCRIPTION_HOST); argParser.addArgument(hostName); port = new IntegerArgument("port", 'p', "port", false, false, true, "{port}", 389, null, MSGID_DESCRIPTION_PORT); argParser.addArgument(port); version = new IntegerArgument("version", 'V', "version", false, false, true, "{version}", 3, null, MSGID_DESCRIPTION_VERSION); argParser.addArgument(version); filename = new StringArgument("filename", 'f', "filename", false, false, true, "{filename}", null, null, MSGID_DELETE_DESCRIPTION_FILENAME); argParser.addArgument(filename); showUsage = new BooleanArgument("showUsage", 'H', "help", MSGID_DESCRIPTION_SHOWUSAGE); argParser.addArgument(showUsage); argParser.setUsageArgument(showUsage); controlStr = new StringArgument("controls", 'J', "controls", false, false, true, "{controloid[:criticality[:value|::b64value|: attrElements = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(valueStr, ", "); while (tokenizer.hasMoreTokens()) { attrElements.add(new ASN1OctetString(tokenizer.nextToken())); } ASN1OctetString controlValue = new ASN1OctetString(new ASN1Sequence(attrElements).encode()); LDAPControl c = new LDAPControl(OID_LDAP_READENTRY_PREREAD, true, controlValue); modifyOptions.getControls().add(c); } if (postReadAttributes.isPresent()) { String valueStr = postReadAttributes.getValue(); ArrayList attrElements = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(valueStr, ", "); while (tokenizer.hasMoreTokens()) { attrElements.add(new ASN1OctetString(tokenizer.nextToken())); } ASN1OctetString controlValue = new ASN1OctetString(new ASN1Sequence(attrElements).encode()); LDAPControl c = new LDAPControl(OID_LDAP_READENTRY_POSTREAD, true, controlValue); modifyOptions.getControls().add(c); } // Set the connection options. connectionOptions.setSASLExternal(saslExternal.isPresent()); if(saslOptions.isPresent()) { LinkedList values = saslOptions.getValues(); for(String saslOption : values) { if(saslOption.startsWith("mech=")) { boolean val = connectionOptions.setSASLMechanism(saslOption); if(val == false) { return 1; } } else { boolean val = connectionOptions.addSASLProperty(saslOption); if(val == false) { return 1; } } } } connectionOptions.setUseSSL(useSSL.isPresent()); connectionOptions.setStartTLS(startTLS.isPresent()); connectionOptions.setReportAuthzID(reportAuthzID.isPresent()); if(connectionOptions.useSASLExternal()) { if(!connectionOptions.useSSL() && !connectionOptions.useStartTLS()) { System.err.println("SASL External requires either SSL or StartTLS " + "options to be requested."); return 1; } if(keyStorePathValue == null) { System.err.println("SASL External requires a path to the SSL " + "client certificate keystore."); return 1; } } try { // Bootstrap and initialize directory data structures. DirectoryServer.bootstrapClient(); // Connect to the specified host with the supplied userDN and password. SSLConnectionFactory sslConnectionFactory = null; if(connectionOptions.useSSL() || connectionOptions.useStartTLS()) { sslConnectionFactory = new SSLConnectionFactory(); sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue, keyStorePasswordValue, trustStorePathValue, trustStorePasswordValue); connectionOptions.setSSLConnectionFactory(sslConnectionFactory); } connection = new LDAPConnection(hostNameValue, portNumber, connectionOptions); connection.connectToHost(bindDNValue, bindPasswordValue); LDAPModify ldapModify = new LDAPModify(fileNameValue); InputStream is = System.in; if(fileNameValue != null) { is = new FileInputStream(fileNameValue); } ldapModify.readAndExecute(connection, is, modifyOptions); } catch(LDAPException le) { assert debugException(CLASS_NAME, "main", le); System.err.println(le.getMessage()); int code = le.getResultCode(); return code; } catch(LDAPConnectionException lce) { assert debugException(CLASS_NAME, "main", lce); System.err.println(lce.getMessage()); int code = lce.getErrorCode(); return code; } catch(Exception e) { assert debugException(CLASS_NAME, "main", e); System.err.println(e.getMessage()); return 1; } finally { if(connection != null) { connection.close(); } } return 0; } }