5046N/A/*
5046N/A * CDDL HEADER START
5046N/A *
5046N/A * The contents of this file are subject to the terms of the
5046N/A * Common Development and Distribution License, Version 1.0 only
5046N/A * (the "License"). You may not use this file except in compliance
5046N/A * with the License.
5046N/A *
5046N/A * You can obtain a copy of the license at
5046N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
5046N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
5046N/A * See the License for the specific language governing permissions
5046N/A * and limitations under the License.
5046N/A *
5046N/A * When distributing Covered Code, include this CDDL HEADER in each
5046N/A * file and include the License file at
5046N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
5046N/A * add the following below this CDDL HEADER, with the fields enclosed
5046N/A * by brackets "[]" replaced with your own identifying information:
5046N/A * Portions Copyright [yyyy] [name of copyright owner]
5046N/A *
5046N/A * CDDL HEADER END
5046N/A *
5046N/A *
5046N/A * Copyright 2010 Sun Microsystems, Inc.
5046N/A */
5046N/A
5046N/Aimport netscape.ldap.util.*;
5046N/A
5046N/Aimport java.util.*;
5046N/Aimport netscape.ldap.*;
5046N/Aimport netscape.ldap.client.*;
5046N/Aimport java.io.*;
5046N/Aimport java.net.*;
5046N/A
5046N/A/**
5046N/A * LDAP Data Interchange Format (LDIF) is a file format used to
5046N/A * import and export directory data from an LDAP server and to
5046N/A * describe a set of changes to be applied to data in a directory.
5046N/A * This format is described in the Internet draft
5046N/A * <A HREF="ftp://ftp.ietf.org/internet-drafts/draft-good-ldap-ldif-00.txt"
5046N/A * TARGET="_blank">The LDAP Data Interchange Format (LDIF) -
5046N/A * Technical Specification</A>.
5046N/A * <P>
5046N/A *
5046N/A * This class implements an LDIF file parser. You can construct
5046N/A * an object of this class to parse data in LDIF format and
5046N/A * manipulate the data as individual <CODE>LDIFRecord</CODE> objects.
5046N/A * <P>
5046N/A *
5046N/A * @version 1.0
5046N/A * @see netscape.ldap.util.LDIFRecord
5046N/A */
5046N/Apublic class LDIF implements Serializable {
5046N/A
5046N/A /**
5046N/A * Internal constants
5046N/A */
5046N/A private final static char COMMENT = '#';
5046N/A static final long serialVersionUID = -2710382547996750924L;
5046N/A
5046N/A /**
5046N/A * Constructs an <CODE>LDIF</CODE> object to parse the
5046N/A * LDAP data read from stdin.
5046N/A * @exception IOException An I/O error has occurred.
5046N/A */
5046N/A public LDIF() throws IOException {
5046N/A DataInputStream ds = new DataInputStream(System.in);
5046N/A BufferedReader d = new BufferedReader(new InputStreamReader(ds, "UTF8"));
5046N/A m_reader = new LineReader(d);
5046N/A m_source = "System.in";
5046N/A m_decoder = new MimeBase64Decoder();
5046N/A }
5046N/A
5046N/A /**
5046N/A * Constructs an <CODE>LDIF</CODE> object to parse the
5046N/A * LDIF data read from a specified file.
5046N/A * @param file the name of the LDIF file to parse
5046N/A * @exception IOException An I/O error has occurred.
5046N/A */
5046N/A public LDIF(String file) throws IOException {
5046N/A FileInputStream fs = new FileInputStream(file);
5046N/A DataInputStream ds = new DataInputStream(fs);
5046N/A BufferedReader d = new BufferedReader(new InputStreamReader(ds, "UTF8"));
5046N/A m_reader = new LineReader(d);
5046N/A m_source = file;
5046N/A m_decoder = new MimeBase64Decoder();
5046N/A }
5046N/A
5046N/A /**
5046N/A * Constructs an <CODE>LDIF</CODE> object to parse the
5046N/A * LDIF data read from an input stream.
5046N/A * @param dstThe input stream providing the LDIF data
5046N/A * @exception IOException An I/O error has occurred.
5046N/A */
5046N/A public LDIF(DataInputStream ds) throws IOException {
5046N/A BufferedReader d = new BufferedReader(new InputStreamReader(ds, "UTF8"));
5046N/A m_reader = new LineReader(d);
5046N/A m_source = ds.toString();
5046N/A m_decoder = new MimeBase64Decoder();
5046N/A }
5046N/A
5046N/A /**
5046N/A * Returns the next record in the LDIF data. You can call this
5046N/A * method repeatedly to iterate through all records in the LDIF data.
5046N/A * <P>
5046N/A *
5046N/A * @return the next record as an <CODE>LDIFRecord</CODE>
5046N/A * object or null if there are no more records.
5046N/A * @exception IOException An I/O error has occurred.
5046N/A * @see netscape.ldap.util.LDIFRecord
5046N/A */
5046N/A public LDIFRecord nextRecord() throws IOException {
5046N/A if ( m_done )
5046N/A return null;
5046N/A else
5046N/A return parse_ldif_record( m_reader );
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses ldif content. The list of attributes is
5046N/A * terminated by \r\n or '-'. This function is
5046N/A * also used to parse the attributes in modifications.
5046N/A * @param ds data input stream
5046N/A */
5046N/A private LDIFRecord parse_ldif_record(LineReader d)
5046N/A throws IOException {
5046N/A String line = null;
5046N/A String dn = null;
5046N/A Vector attrs = new Vector();
5046N/A LDIFRecord rec = null;
5046N/A
5046N/A // Skip past any blank lines
5046N/A while( ((line = d.readLine()) != null) &&
5046N/A (line.length() < 1) ) {
5046N/A }
5046N/A if (line == null) {
5046N/A return null;
5046N/A }
5046N/A
5046N/A if (line.toLowerCase().startsWith("version:")) {
5046N/A m_version = Integer.parseInt(
5046N/A line.substring("version:".length()).trim() );
5046N/A if ( m_version != 1 ) {
5046N/A throwLDIFException( "Unexpected " + line );
5046N/A }
5046N/A // Do the next record
5046N/A line = d.readLine();
5046N/A if ( (line != null) && (line.length() == 0) ) {
5046N/A // Skip the newline
5046N/A line = d.readLine();
5046N/A }
5046N/A if (line == null) {
5046N/A return null;
5046N/A }
5046N/A }
5046N/A
5046N/A if (!line.toLowerCase().startsWith("dn:"))
5046N/A throwLDIFException("expecting dn:");
5046N/A dn = line.substring(3).trim();
5046N/A if (dn.startsWith(":") && (dn.length() > 1)) {
5046N/A String substr = dn.substring(1).trim();
5046N/A dn = new String(getDecodedBytes(substr), "UTF8");
5046N/A }
5046N/A
5046N/A LDIFContent content = parse_ldif_content(d);
5046N/A rec = new LDIFRecord(dn, content);
5046N/A return rec;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses ldif content. The list of attributes is
5046N/A * terminated by \r\n or '-'. This function is
5046N/A * also used to parse the attributes in modifications.
5046N/A * @param ds data input stream
5046N/A */
5046N/A private LDIFContent parse_ldif_content(LineReader d)
5046N/A throws IOException {
5046N/A String line = d.readLine();
5046N/A if ((line == null) || (line.length() < 1) || (line.equals("-"))) {
5046N/A // if this is empty line, then we're finished reading all
5046N/A // the info for the current entry
5046N/A if ((line != null) && (line.length() < 1)) {
5046N/A m_currEntryDone = true;
5046N/A }
5046N/A return null;
5046N/A }
5046N/A
5046N/A if (line.toLowerCase().startsWith("changetype:")) {
5046N/A /* handles (changerecord) */
5046N/A LDIFContent lc = null;
5046N/A String changetype = line.substring(11).trim();
5046N/A if (changetype.equals("modify")) {
5046N/A lc = parse_mod_spec(d);
5046N/A } else if (changetype.equals("add")) {
5046N/A lc = parse_add_spec(d);
5046N/A } else if (changetype.equals("delete")) {
5046N/A lc = parse_delete_spec(d);
5046N/A } else if (changetype.equals("moddn") ||
5046N/A changetype.equals("modrdn")) {
5046N/A lc = parse_moddn_spec(d);
5046N/A } else {
5046N/A throwLDIFException("change type not supported");
5046N/A }
5046N/A return lc;
5046N/A }
5046N/A
5046N/A /* handles 1*(attrval-spec) */
5046N/A Hashtable ht = new Hashtable();
5046N/A String newtype = null;
5046N/A Object val = null;
5046N/A LDAPAttribute newAttr = null;
5046N/A Vector controlVector = null;
5046N/A
5046N/A /* Read lines until we're past the record */
5046N/A while( true ) {
5046N/A if (line.toLowerCase().startsWith("control:")) {
5046N/A if ( controlVector == null ) {
5046N/A controlVector = new Vector();
5046N/A }
5046N/A controlVector.addElement( parse_control_spec( line ) );
5046N/A } else {
5046N/A /* An attribute */
5046N/A int len = line.length();
5046N/A if ( len < 1 ) {
5046N/A break;
5046N/A }
5046N/A int idx = line.indexOf(':');
5046N/A /* Must have a colon */
5046N/A if (idx == -1)
5046N/A throwLDIFException("no ':' found");
5046N/A /* attribute type */
5046N/A newtype = line.substring(0,idx).toLowerCase();
5046N/A val = "";
5046N/A /* Could be :: for binary */
5046N/A idx++;
5046N/A if ( len > idx ) {
5046N/A if ( line.charAt(idx) == ':' ) {
5046N/A idx++;
5046N/A String substr = line.substring(idx).trim();
5046N/A val = getDecodedBytes(substr);
5046N/A } else if (line.charAt(idx) == '<') {
5046N/A try {
5046N/A URL url =
5046N/A new URL(line.substring(idx+1).trim());
5046N/A String filename = url.getFile();
5046N/A val = getFileContent(filename);
5046N/A } catch (MalformedURLException ex) {
5046N/A throwLDIFException(
5046N/A ex +
5046N/A ": cannot construct url "+
5046N/A line.substring(idx+1).trim());
5046N/A }
5046N/A } else {
5046N/A val = line.substring(idx).trim();
5046N/A }
5046N/A }
5046N/A /* Is there a previous value for this attribute? */
5046N/A newAttr = (LDAPAttribute)ht.get( newtype );
5046N/A if ( newAttr == null ) {
5046N/A newAttr = new LDAPAttribute( newtype );
5046N/A }
5046N/A if ( val instanceof String ) {
5046N/A newAttr.addValue( (String)val );
5046N/A } else {
5046N/A newAttr.addValue( (byte[])val );
5046N/A }
5046N/A ht.put( newtype, newAttr );
5046N/A }
5046N/A line = d.readLine();
5046N/A if (line == null || (line.length() < 1) ||
5046N/A (line.equals("-"))) {
5046N/A if ((line != null) && (line.length() < 1)) {
5046N/A m_currEntryDone = true;
5046N/A }
5046N/A break;
5046N/A }
5046N/A }
5046N/A LDIFAttributeContent ac = new LDIFAttributeContent();
5046N/A // Copy over the attributes to the record
5046N/A Enumeration en = ht.elements();
5046N/A while( en.hasMoreElements() ) {
5046N/A ac.addElement( (LDAPAttribute)en.nextElement() );
5046N/A }
5046N/A ht.clear();
5046N/A if( controlVector != null ) {
5046N/A LDAPControl[] controls =
5046N/A new LDAPControl[controlVector.size()];
5046N/A controlVector.copyInto( controls );
5046N/A ac.setControls( controls );
5046N/A controlVector.removeAllElements();
5046N/A }
5046N/A return ac;
5046N/A }
5046N/A
5046N/A private byte[] getDecodedBytes(String line) {
5046N/A ByteBuf inBuf = new ByteBuf(line);
5046N/A ByteBuf decodedBuf = new ByteBuf();
5046N/A /* Translate from base 64 */
5046N/A m_decoder.translate( inBuf, decodedBuf );
5046N/A return decodedBuf.toBytes();
5046N/A }
5046N/A
5046N/A private byte[] getFileContent(String url) throws IOException {
5046N/A StringTokenizer tokenizer = new StringTokenizer(url, "|");
5046N/A String filename = url;
5046N/A int num = tokenizer.countTokens();
5046N/A if (num == 2) {
5046N/A String token = (String)tokenizer.nextElement();
5046N/A int index = token.lastIndexOf("/");
5046N/A String drive = token.substring(index+1);
5046N/A token = (String)tokenizer.nextElement();
5046N/A token = token.replace('/', '\\');
5046N/A filename = drive+":"+token;
5046N/A }
5046N/A
5046N/A File file = new File(filename);
5046N/A byte[] b = new byte[(int)file.length()];
5046N/A FileInputStream fi = new FileInputStream(filename);
5046N/A fi.read(b);
5046N/A return b;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses add content
5046N/A * @param ds data input stream
5046N/A */
5046N/A private LDIFAddContent parse_add_spec(LineReader d)
5046N/A throws IOException {
5046N/A LDIFAttributeContent ac = (LDIFAttributeContent)parse_ldif_content(d);
5046N/A if (m_currEntryDone)
5046N/A m_currEntryDone = false;
5046N/A LDAPAttribute attrs[] = ac.getAttributes();
5046N/A LDIFAddContent rc = new LDIFAddContent(attrs);
5046N/A LDAPControl[] controls = ac.getControls();
5046N/A if ( controls != null ) {
5046N/A rc.setControls( controls );
5046N/A }
5046N/A return rc;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses delete content
5046N/A * @param ds data input stream
5046N/A */
5046N/A private LDIFDeleteContent parse_delete_spec(LineReader d)
5046N/A throws IOException {
5046N/A Vector controlVector = null;
5046N/A LDIFDeleteContent dc = new LDIFDeleteContent();
5046N/A String line = d.readLine();
5046N/A while( line != null && !line.equals("") ) {
5046N/A if (line.toLowerCase().startsWith("control:")) {
5046N/A if ( controlVector == null ) {
5046N/A controlVector = new Vector();
5046N/A }
5046N/A controlVector.addElement( parse_control_spec( line ) );
5046N/A } else {
5046N/A throwLDIFException("invalid SEP" );
5046N/A }
5046N/A line = d.readLine();
5046N/A }
5046N/A if( controlVector != null ) {
5046N/A LDAPControl[] controls = new LDAPControl[controlVector.size()];
5046N/A controlVector.copyInto( controls );
5046N/A dc.setControls( controls );
5046N/A controlVector.removeAllElements();
5046N/A }
5046N/A
5046N/A return dc;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses change modification.
5046N/A * @param ds data input stream
5046N/A */
5046N/A private LDIFModifyContent parse_mod_spec(LineReader d)
5046N/A throws IOException {
5046N/A
5046N/A Vector controlVector = null;
5046N/A String line = null;
5046N/A line = d.readLine();
5046N/A LDIFModifyContent mc = new LDIFModifyContent();
5046N/A do {
5046N/A int oper = -1;
5046N/A if (line.toLowerCase().startsWith("add:")) {
5046N/A oper = LDAPModification.ADD;
5046N/A } else if (line.toLowerCase().startsWith("delete:")) {
5046N/A oper = LDAPModification.DELETE;
5046N/A } else if (line.toLowerCase().startsWith("replace:")) {
5046N/A oper = LDAPModification.REPLACE;
5046N/A } else
5046N/A throwLDIFException("unknown modify type");
5046N/A
5046N/A LDIFAttributeContent ac =
5046N/A (LDIFAttributeContent)parse_ldif_content(d);
5046N/A if (ac != null) {
5046N/A LDAPAttribute attrs[] = ac.getAttributes();
5046N/A for (int i = 0; i < attrs.length; i++) {
5046N/A LDAPModification mod = new LDAPModification(oper, attrs[i]);
5046N/A mc.addElement( mod );
5046N/A }
5046N/A LDAPControl[] controls = ac.getControls();
5046N/A if ( controls != null ) {
5046N/A if ( controlVector == null ) {
5046N/A controlVector = new Vector();
5046N/A }
5046N/A for( int i = 0; i < controls.length; i++ ) {
5046N/A controlVector.addElement( controls[i] );
5046N/A }
5046N/A }
5046N/A // if there is no attrval-spec, go into the else statement
5046N/A } else {
5046N/A int index = line.indexOf(":");
5046N/A if (index == -1)
5046N/A throwLDIFException("colon missing in "+line);
5046N/A
5046N/A String attrName = line.substring(index+1).trim();
5046N/A
5046N/A if (oper == LDAPModification.ADD)
5046N/A throwLDIFException("add operation needs the value for attribute "+attrName);
5046N/A LDAPAttribute attr = new LDAPAttribute(attrName);
5046N/A LDAPModification mod = new LDAPModification(oper, attr);
5046N/A mc.addElement(mod);
5046N/A }
5046N/A if (m_currEntryDone) {
5046N/A m_currEntryDone = false;
5046N/A break;
5046N/A }
5046N/A line = d.readLine();
5046N/A } while (line != null && !line.equals(""));
5046N/A
5046N/A if( controlVector != null ) {
5046N/A LDAPControl[] controls = new LDAPControl[controlVector.size()];
5046N/A controlVector.copyInto( controls );
5046N/A mc.setControls( controls );
5046N/A controlVector.removeAllElements();
5046N/A }
5046N/A return mc;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses moddn/modrdn modification.
5046N/A * @param d data input stream
5046N/A */
5046N/A private LDIFModDNContent parse_moddn_spec(LineReader d)
5046N/A throws IOException {
5046N/A Vector controlVector = null;
5046N/A String line = null;
5046N/A line = d.readLine();
5046N/A LDIFModDNContent mc = new LDIFModDNContent();
5046N/A String val = null;
5046N/A do {
5046N/A if (line.toLowerCase().startsWith("newrdn:") &&
5046N/A (line.length() > ("newrdn:".length()+1))) {
5046N/A mc.setRDN(line.substring("newrdn:".length()).trim());
5046N/A } else if (line.toLowerCase().startsWith("deleteoldrdn:") &&
5046N/A (line.length() > ("deleteoldrdn:".length()+1))) {
5046N/A String str = line.substring("deleteoldrdn:".length()).trim();
5046N/A if (str.equals("0") || str.equalsIgnoreCase("false"))
5046N/A mc.setDeleteOldRDN(false);
5046N/A else if (str.equals("1") || str.equalsIgnoreCase("true"))
5046N/A mc.setDeleteOldRDN(true);
5046N/A else
5046N/A throwLDIFException("Incorrect input for deleteOldRdn ");
5046N/A } else if (line.toLowerCase().startsWith("newsuperior:") &&
5046N/A (line.length() > ("newsuperior:".length()+1))) {
5046N/A mc.setNewParent(line.substring(
5046N/A "newsuperior:".length()).trim());
5046N/A } else if (line.toLowerCase().startsWith("newparent:") &&
5046N/A (line.length() > ("newparent:".length()+1))) {
5046N/A mc.setNewParent(line.substring(
5046N/A "newparent:".length()).trim());
5046N/A } else if (line.toLowerCase().startsWith("control:")) {
5046N/A if ( controlVector == null ) {
5046N/A controlVector = new Vector();
5046N/A }
5046N/A controlVector.addElement( parse_control_spec( line ) );
5046N/A }
5046N/A line = d.readLine();
5046N/A } while (line != null && !line.equals(""));
5046N/A
5046N/A if( controlVector != null ) {
5046N/A LDAPControl[] controls = new LDAPControl[controlVector.size()];
5046N/A controlVector.copyInto( controls );
5046N/A mc.setControls( controls );
5046N/A controlVector.removeAllElements();
5046N/A }
5046N/A
5046N/A return mc;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Parses the specification of a control<BR>
5046N/A *
5046N/A * A control looks line one of the following:
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210 true
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210 true: someASCIIvalue
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210: someASCIIvalue
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210 true:: 44GK44GM44GV44KP44KJ
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210:: 44GK44GM44GV44KP44KJ
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210 true:< file:///usr/local/directory/cont.dta
5046N/A *<BR>
5046N/A * control: 1.2.3.4.10.210:< file:///usr/local/directory/cont.dta
5046N/A *
5046N/A * @param line a line containing a control spec
5046N/A * @return a parsed control.
5046N/A * @exception IOException if the line could not be parsed
5046N/A */
5046N/A protected LDAPControl parse_control_spec( String line )
5046N/A throws IOException {
5046N/A boolean criticality = true;
5046N/A String OID;
5046N/A byte[] val = null;
5046N/A int len = line.length();
5046N/A int idx = line.indexOf(':') + 2;
5046N/A /* OID, must be present */
5046N/A if ( idx >= len ) {
5046N/A throwLDIFException("OID required for control");
5046N/A }
5046N/A line = line.substring(idx).trim();
5046N/A idx = line.indexOf(' ');
5046N/A if ( idx < 0 ) {
5046N/A OID = line;
5046N/A } else {
5046N/A /* Optional criticality */
5046N/A OID = line.substring(0, idx);
5046N/A line = line.substring(idx+1);
5046N/A idx = line.indexOf(':');
5046N/A String criticalVal;
5046N/A if (idx > 0) {
5046N/A criticalVal = line.substring(0, idx);
5046N/A } else {
5046N/A criticalVal = line;
5046N/A }
5046N/A if ( criticalVal.compareTo("true") == 0 ) {
5046N/A criticality = true;
5046N/A } else if ( criticalVal.compareTo("false") == 0 ) {
5046N/A criticality = false;
5046N/A } else {
5046N/A throwLDIFException(
5046N/A "Criticality for control must be true" +
5046N/A " or false, not " + criticalVal);
5046N/A }
5046N/A /* Optional value */
5046N/A if ( idx > 0 ) {
5046N/A /* Could be :: for binary */
5046N/A idx++;
5046N/A if ( line.length() > idx ) {
5046N/A if ( line.charAt(idx) == ':' ) {
5046N/A idx++;
5046N/A line = line.substring(idx).trim();
5046N/A val = getDecodedBytes(line);
5046N/A } else if (line.charAt(idx) == '<') {
5046N/A String urlString = line.substring(idx+1).trim();
5046N/A try {
5046N/A URL url = new URL(urlString);
5046N/A String filename = url.getFile();
5046N/A val = getFileContent(filename);
5046N/A } catch (MalformedURLException ex) {
5046N/A throwLDIFException(
5046N/A ex + ": cannot construct url "+
5046N/A urlString);
5046N/A }
5046N/A } else {
5046N/A try {
5046N/A val = line.substring(idx).trim().getBytes("UTF8");
5046N/A } catch(Exception x) {
5046N/A }
5046N/A }
5046N/A }
5046N/A }
5046N/A }
5046N/A return new LDAPControl( OID, criticality, val );
5046N/A }
5046N/A
5046N/A /**
5046N/A * Returns true if all the bytes in the given array are valid for output as a
5046N/A * String according to the LDIF specification. If not, the array should
5046N/A * output base64-encoded.
5046N/A * @return <code>true</code> if all the bytes in the given array are valid for
5046N/A * output as a String according to the LDIF specification; otherwise,
5046N/A * <code>false</code>.
5046N/A */
5046N/A public static boolean isPrintable(byte[] b) {
5046N/A for( int i = b.length - 1; i >= 0; i-- ) {
5046N/A if ( (b[i] < ' ') || (b[i] > 127) ) {
5046N/A if ( b[i] != '\t' )
5046N/A return false;
5046N/A }
5046N/A }
5046N/A return true;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Outputs the String in LDIF line-continuation format. No line will be longer
5046N/A * than the given max. A continuation line starts with a single blank space.
5046N/A * @param pw the printer writer
5046N/A * @param value the given string being printed out
5046N/A * @param max the maximum characters allowed in the line
5046N/A */
5046N/A public static void breakString( PrintWriter pw, String value, int max) {
5046N/A int leftToGo = value.length();
5046N/A int written = 0;
5046N/A int maxChars = max;
5046N/A /* Limit to 77 characters per line */
5046N/A while( leftToGo > 0 ) {
5046N/A int toWrite = Math.min( maxChars, leftToGo );
5046N/A String s = value.substring( written, written+toWrite);
5046N/A if ( written != 0 ) {
5046N/A pw.print( " " + s );
5046N/A } else {
5046N/A pw.print( s );
5046N/A maxChars -= 1;
5046N/A }
5046N/A written += toWrite;
5046N/A leftToGo -= toWrite;
5046N/A /* Don't use pw.println, because it outputs an extra CR
5046N/A in Win32 */
5046N/A pw.print( '\n' );
5046N/A }
5046N/A }
5046N/A
5046N/A /**
5046N/A * Gets the version of LDIF used in the data.
5046N/A * @return version of LDIF used in the data.
5046N/A */
5046N/A public int getVersion() {
5046N/A return m_version;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Gets the string representation of the
5046N/A * entire LDIF file.
5046N/A * @return the string representation of the entire LDIF data file.
5046N/A */
5046N/A public String toString() {
5046N/A return "LDIF {" + m_source + "}";
5046N/A }
5046N/A
5046N/A /**
5046N/A * Throws a LDIF file exception including the current line number.
5046N/A * @param msg Error message
5046N/A */
5046N/A protected void throwLDIFException(String msg)throws IOException {
5046N/A throw new IOException ("line " +
5046N/A (m_currLineNum-m_continuationLength) + ": " + msg);
5046N/A }
5046N/A
5046N/A /**
5046N/A * Internal variables
5046N/A */
5046N/A private int m_version = 1;
5046N/A private boolean m_done = false;
5046N/A private LineReader m_reader = null;
5046N/A private String m_source = null;
5046N/A private MimeBase64Decoder m_decoder = null;
5046N/A private boolean m_currEntryDone = false;
5046N/A private int m_currLineNum;
5046N/A private int m_continuationLength;
5046N/A
5046N/A /* Concatenate continuation lines, if present */
5046N/A class LineReader {
5046N/A LineReader( BufferedReader d ) {
5046N/A _d = d;
5046N/A }
5046N/A /**
5046N/A * Reads a non-comment line.
5046N/A * @return a string or null.
5046N/A */
5046N/A String readLine() throws IOException {
5046N/A String line = null;
5046N/A String result = null;
5046N/A int readCnt = 0, continuationLength = 0;
5046N/A do {
5046N/A /* Leftover line from last time? */
5046N/A if ( _next != null ) {
5046N/A line = _next;
5046N/A _next = null;
5046N/A } else {
5046N/A line = _d.readLine();
5046N/A }
5046N/A if (line != null) {
5046N/A readCnt++;
5046N/A /* Empty line means end of record */
5046N/A if( line.length() < 1 ) {
5046N/A if ( result == null )
5046N/A result = line;
5046N/A else {
5046N/A _next = line;
5046N/A break;
5046N/A }
5046N/A } else if( line.charAt(0) == COMMENT ) {
5046N/A /* Ignore comment lines */
5046N/A } else if( line.charAt(0) != ' ' ) {
5046N/A /* Not a continuation line */
5046N/A if( result == null ) {
5046N/A result = line;
5046N/A } else {
5046N/A _next = line;
5046N/A break;
5046N/A }
5046N/A } else {
5046N/A /* Continuation line */
5046N/A if ( result == null ) {
5046N/A m_currLineNum += readCnt;
5046N/A throwLDIFException("continuation out of nowhere");
5046N/A }
5046N/A result += line.substring(1);
5046N/A continuationLength++;
5046N/A }
5046N/A } else {
5046N/A /* End of file */
5046N/A break;
5046N/A }
5046N/A } while ( true );
5046N/A
5046N/A m_done = ( line == null );
5046N/A
5046N/A m_currLineNum += readCnt;
5046N/A if (_next != null) {
5046N/A // read one line ahead
5046N/A m_currLineNum--;
5046N/A }
5046N/A m_continuationLength = continuationLength;
5046N/A
5046N/A return result;
5046N/A }
5046N/A private BufferedReader _d;
5046N/A String _next = null;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Converts a byte array to a printable string following
5046N/A * the LDIF rules (encode in base64 if necessary)
5046N/A *
5046N/A * @param b the byte array to convert
5046N/A * @return a converted string which is printable.
5046N/A */
5046N/A public static String toPrintableString( byte[] b ) {
5046N/A String s = "";
5046N/A if (isPrintable(b)) {
5046N/A try {
5046N/A s = new String(b, "UTF8");
5046N/A } catch ( java.io.UnsupportedEncodingException e ) {
5046N/A }
5046N/A } else {
5046N/A ByteBuf inBuf = new ByteBuf( b, 0, b.length );
5046N/A ByteBuf encodedBuf = new ByteBuf();
5046N/A // Translate to base 64
5046N/A MimeBase64Encoder encoder = new MimeBase64Encoder();
5046N/A encoder.translate( inBuf, encodedBuf );
5046N/A int nBytes = encodedBuf.length();
5046N/A if ( nBytes > 0 ) {
5046N/A s = new String(encodedBuf.toBytes(), 0, nBytes);
5046N/A }
5046N/A }
5046N/A return s;
5046N/A }
5046N/A
5046N/A /**
5046N/A * Test driver - just reads and parses an LDIF file, printing
5046N/A * each record as interpreted
5046N/A *
5046N/A * @param args name of the LDIF file to parse
5046N/A */
5046N/A public static void main( String[] args ) {
5046N/A if ( args.length != 1 ) {
5046N/A System.out.println( "Usage: java LDIF <FILENAME>" );
5046N/A System.exit( 1 );
5046N/A }
5046N/A LDIF ldif = null;
5046N/A try {
5046N/A ldif = new LDIF( args[0] );
5046N/A } catch (Exception e) {
5046N/A System.err.println("Failed to read LDIF file " + args[0] +
5046N/A ", " + e.toString());
5046N/A System.exit(1);
5046N/A }
5046N/A try {
5046N/A for( LDIFRecord rec = ldif.nextRecord();
5046N/A rec != null; rec = ldif.nextRecord() ) {
5046N/A System.out.println( rec.toString() + '\n' );
5046N/A }
5046N/A } catch ( IOException ex ) {
5046N/A System.out.println( ex );
5046N/A System.exit( 1 );
5046N/A }
5046N/A System.exit( 0 );
5046N/A }
5046N/A}