package jeffaschenk.commons.frameworks.cnxidx.utility; import jeffaschenk.commons.frameworks.cnxidx.utility.ldap.idxIRRBase64; import java.io.*; import java.nio.*; import java.nio.channels.*; import java.net.URL; import java.net.MalformedURLException; import javax.naming.directory.*; /** * Java Class to process and read as input an LDIF file or stream. * Functionality for LDIF version detection, * comments and other enhancements and fixes applied for compliance with * IETF RFC2849 - LDAP Data Interchange Format. * * @author jeff.schenk * @version 3.0 $Revision * Developed 2001-2003 */ public class idxLDIFReader { private String myCurrent_DN = null; private BufferedReader myin = null; private String LDIFVersion = ""; private boolean hasMore = true; private boolean First_Line = true; /** * Initial Constructor. */ public idxLDIFReader(BufferedReader in) { myin = in; } // End of Constructor. /** * Obtains the next LDIF Entry found in our BufferedReader * input. Will return an Attributes Object, containing all * of the Attributes for the obtained entry. * * @return Attributes All Attributes for the current entry. * @throws java.io.IOException if problems reading BufferedReader */ public Attributes getNextEntry() throws IOException { String inputline = null; String attributeName = null; StringBuffer entry_value = null; StringBuffer entry_dn = null; Attributes entry = null; boolean entry_value_encoded = false; boolean entry_value_has_reference = false; int position; int from; int to; // ******************************************* // Check the End of Input. if (!hasMore) { return (null); } // ******************************************* // Process the Incoming LDIF Data. while (hasMore) { // ********************************************* // Read an Input Line. // Force the rest of the data out, if we reach EOF // before a End of Entry deliminter. inputline = myin.readLine(); if (inputline == null) { inputline = ""; hasMore = false; } // End of inputline NULL Check. // ********************************************* // If this is our first line, check for a version // indication. if (First_Line) { if (checkForVersion(inputline)) { continue; } } // End of First Line // ********************************************* // If a Comment Ignore it. if ((inputline.length() != 0) && (inputline.charAt(0) == '#')) { continue; } // ************************************************* // Do I have a new Attribute and not a Continuation? position = inputline.indexOf(":"); if (position != -1 && inputline.charAt(0) != ' ') { // ************************************** // Save out Previous Attribute if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(true); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); } // End of if not null attribute or value. to = position; from = position + 1; if (inputline.length() > from) { if (inputline.charAt(from) == ':') { entry_value_encoded = true; from++; } else { entry_value_encoded = false; } // End of Else if (inputline.charAt(from) == '<') { entry_value_has_reference = true; from++; } else { entry_value_has_reference = false; } // End of Else. if (inputline.charAt(from) == ' ') { from++; } attributeName = inputline.substring(0, to).toLowerCase(); entry_value = new StringBuffer(inputline.substring(from)); } else { attributeName = inputline.substring(0, to).toLowerCase(); entry_value = new StringBuffer(""); } if (entry_dn == null) { entry_dn = entry_value; attributeName = null; } // End of If. // ************************************************* // Do I have an Entry Seperator Line? } else if (inputline.length() == 0) { if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(true); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); attributeName = null; entry_value = null; } // End of If. // has no dn? if (entry_dn == null) { continue; } myCurrent_DN = entry_dn.toString(); return (entry); } else if (inputline.charAt(0) == ' ') { entry_value.append(inputline.substring(1)); } // end of Else if. } // End of While. hasMore = false; myCurrent_DN = null; return null; } // End of getNextEntry Method /** * Obtains the next LDIF Entry found in our BufferedReader * input, this method Preserves Case for the Attribute Name or ID. * Will return an Attributes Object, containing all * of the Attributes for the obtained entry. * * @return Attributes All Attributes for the current entry. * @throws java.io.IOException if problems reading BufferedReader */ public Attributes getNextEntryPreserveCase() throws IOException { String inputline = null; String attributeName = null; StringBuffer entry_value = null; StringBuffer entry_dn = null; Attributes entry = null; boolean entry_value_encoded = false; boolean entry_value_has_reference = false; int position; int from; int to; // ******************************************* // Check the End of Input. if (!hasMore) { return (null); } // ******************************************* // Process the Incoming LDIF Data. while (hasMore) { // ********************************************* // Read an Input Line. // Force the rest of the data out, if we reach EOF // before a End of Entry deliminter. inputline = myin.readLine(); if (inputline == null) { inputline = ""; hasMore = false; } // End of inputline NULL Check. // ********************************************* // If this is our first line, check for a version // indication. if (First_Line) { if (checkForVersion(inputline)) { continue; } } // End of First Line // ********************************************* // If a Comment Ignore it. if ((inputline.length() != 0) && (inputline.charAt(0) == '#')) { continue; } // ************************************************* // Do I have a new Attribute and not a Continuation? position = inputline.indexOf(":"); if (position != -1 && inputline.charAt(0) != ' ') { // ************************************** // Save out Previous Attribute if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(false); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); } // End of if not null attribute or value. to = position; from = position + 1; if (inputline.length() > from) { if (inputline.charAt(from) == ':') { entry_value_encoded = true; from++; } else { entry_value_encoded = false; } // End of Else if (inputline.charAt(from) == '<') { entry_value_has_reference = true; from++; } else { entry_value_has_reference = false; } // End of Else. if (inputline.charAt(from) == ' ') { from++; } attributeName = inputline.substring(0, to); entry_value = new StringBuffer(inputline.substring(from)); } else { attributeName = inputline.substring(0, to); entry_value = new StringBuffer(""); } if (entry_dn == null) { entry_dn = entry_value; attributeName = null; } // End of If. // ************************************************* // Do I have an Entry Seperator Line? } else if (inputline.length() == 0) { if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(false); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); attributeName = null; entry_value = null; } // End of If. // has no dn? if (entry_dn == null) { continue; } myCurrent_DN = entry_dn.toString(); return (entry); } else if (inputline.charAt(0) == ' ') { entry_value.append(inputline.substring(1)); } // end of Else if. } // End of While. hasMore = false; myCurrent_DN = null; return null; } // End of getNextEntryPreserveCase Method /** * Obtains the next LDIF Entry found in our BufferedReader * input. Will return an Attributes Object, containing all * of the Attributes for the current Modification structure * for the obtained entry. * * @return Attributes Current Modification Attributes * for the current entry. * @throws java.io.IOException if problems reading BufferedReader */ public Attributes getNextModEntry() throws IOException { String inputline = null; String attributeName = null; StringBuffer entry_value = null; StringBuffer entry_dn = null; Attributes entry = null; boolean entry_value_encoded = false; boolean entry_value_has_reference = false; int position; int from; int to; // ******************************************* // Check the End of Input. if (!hasMore) { return (null); } // ******************************************* // Process the Incoming LDIF Data. while (hasMore) { // ********************************************* // Read an Input Line. // Force the rest of the data out, if we reach EOF // before a End of Entry deliminter. inputline = myin.readLine(); if (inputline == null) { inputline = ""; hasMore = false; } // End of inputline NULL Check. // ********************************************* // If this is our first line, check for a version // indication. if (First_Line) { if (checkForVersion(inputline)) { continue; } } // End of First Line // ********************************************* // If a Comment Ignore it. if ((inputline.length() != 0) && (inputline.charAt(0) == '#')) { continue; } // ************************************************* // Do I have a new Attribute and not a Continuation? position = inputline.indexOf(":"); if (position != -1 && inputline.charAt(0) != ' ') { // ************************************** // Save out Previous Attribute if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(true); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); if (attributeName.equalsIgnoreCase("dn")) { entry_dn = entry_value; myCurrent_DN = entry_dn.toString(); } // End of If. } // End of if not null attribute or value. to = position; from = position + 1; if (inputline.length() > from) { if (inputline.charAt(from) == ':') { entry_value_encoded = true; from++; } else { entry_value_encoded = false; } // End of Else if (inputline.charAt(from) == '<') { entry_value_has_reference = true; from++; } else { entry_value_has_reference = false; } // End of Else. if (inputline.charAt(from) == ' ') { from++; } attributeName = inputline.substring(0, to).toLowerCase(); entry_value = new StringBuffer(inputline.substring(from)); } else { attributeName = inputline.substring(0, to).toLowerCase(); entry_value = new StringBuffer(""); } // End of Inner Else. // ************************************************* // Do I have end of a Modification Seperator? } else if ((inputline.length() != 0) && (inputline.charAt(0) == '-')) { // ************************************** // Save out Previous Attribute if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(true); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); if (attributeName.equalsIgnoreCase("dn")) { entry_dn = entry_value; myCurrent_DN = entry_dn.toString(); } // End of If. attributeName = null; entry_value = null; } // End of If. // ********************************** // Has no dn? if (myCurrent_DN == null) { continue; } return (entry); // ************************************************* // Do I have an Entry Seperator Line? } else if (inputline.length() == 0) { // ************************************** // Save out Previous Attribute if (attributeName != null && entry_value != null) { if (entry == null) { entry = new BasicAttributes(true); } add(entry, attributeName, entry_value.toString(), entry_value_encoded, entry_value_has_reference); if (attributeName.equalsIgnoreCase("dn")) { entry_dn = entry_value; myCurrent_DN = entry_dn.toString(); } // End of If. attributeName = null; entry_value = null; } // End of If. // ********************************** // Has no dn? if (myCurrent_DN == null) { continue; } return (entry); // ************************************************* // Do I have an Attribute Continuation? } else if (inputline.charAt(0) == ' ') { entry_value.append(inputline.substring(1)); } // end of Else if. } // End of While. hasMore = false; myCurrent_DN = null; return null; } // End of getNextModEntry Method /** * Obtains the current DN found during our getNextEntry * method. * * @return String of current DN. */ public String getCurrentDN() { return (myCurrent_DN); } // End of getCurrentDN Method. /** * Provides an Iteration indicator method. * * @return boolean indicator if additional entries need * to be processed. */ public boolean hasMore() { return (hasMore); } // End of hasMore Method. /** * Obtains detected LDIF Version. * * @return String of LDIF Version. */ public String getVersion() { return (LDIFVersion); } // End of getVersion Method. /** * Private method for creating Attributes Object of * current LDIF entry being formulated. */ private void add(Attributes entry, String attribute, String value, boolean encoded, boolean reference) { Attribute vals = entry.get(attribute); if (vals == null) { vals = new BasicAttribute(attribute); } if (encoded) { vals.add(idxIRRBase64.decode(value.toCharArray())); } else if (reference) { vals.add(dereferenceValueWithNIO(value)); } else { vals.add(value); } // *************************** // Place Value into Attributes // Entry. entry.put(vals); } // End of Private add Method. /** * Private method for dereferencing an Attribute from * a Specified URL. */ private byte[] dereferenceValue(String _inrefurl) { // ********************************************* // Use an InputStream to obtain the File on the // local filesystem only. try { // ******************************** // We shall only accept local // File Path Specifications // with the URL. URL url = new URL(_inrefurl); String protocol = url.getProtocol(); if ((protocol == null) || (!protocol.equalsIgnoreCase("file"))) { return (_inrefurl.getBytes()); } String filename = url.getFile(); if ((filename == null) || (filename.equalsIgnoreCase(""))) { return (_inrefurl.getBytes()); } // *********************************** // Create a File Object for this file. File f = new File(filename); if (!f.exists()) { return (_inrefurl.getBytes()); } // ************************************** // Check to verify the length. Long filesize = new Long(f.length()); if (filesize.compareTo(new Long(Integer.MAX_VALUE)) > 0) { // Since our Directory can not accept a // Attribute value more than 2BG, simple // just drop in the URL. return (_inrefurl.getBytes()); } // End of If. // ************************************** // Open up a File Stream. BufferedInputStream bins = new BufferedInputStream(new FileInputStream(f), 16384); // *********************************** // Create a Byte Array for the file // and read the entire file in one // operation. byte[] buffer = new byte[(int) f.length()]; bins.read(buffer, 0, buffer.length); // *************************** // Close the Streams bins.close(); // ************************************** // Obtain the File as a Byte Array from // our input Buffer. return (buffer); // *********************************** // Any Exceptions, we shall // simple return the incoming // Reference URL. } catch (final MalformedURLException e) { return (_inrefurl.getBytes()); } catch (final IOException e) { return (_inrefurl.getBytes()); } // end of Exception clause. } // End of Private dereferenceValue Method. /** * Private method for dereferencing an Attribute from * a Specified URL. */ private byte[] dereferenceValueWithNIO(String _inrefurl) { // *************************************** // Use NIO to obtain the File on the // local filesystem only. Long filesize = new Long(0); ByteBuffer buffer = null; try { // ******************************** // We shall only accept local // File Path Specifications // with the URL. URL url = new URL(_inrefurl); String protocol = url.getProtocol(); if ((protocol == null) || (!protocol.equalsIgnoreCase("file"))) { return (_inrefurl.getBytes()); } String filename = url.getFile(); if ((filename == null) || (filename.equalsIgnoreCase(""))) { return (_inrefurl.getBytes()); } // *********************************** // Open up a File Stream and a // NIO Channel. FileInputStream fin = new FileInputStream(filename); FileChannel fc = fin.getChannel(); filesize = new Long(fc.size()); if (filesize.compareTo(new Long(Integer.MAX_VALUE)) > 0) { // Since our Directory can not accept a // Attribute value more than 2BG, simple // just drop in the URL. return (_inrefurl.getBytes()); } // End of If. // **************************** // Create a ByteBuffer. buffer = ByteBuffer.allocate(filesize.intValue()); fc.read(buffer); buffer.flip(); // Reposition to Beginning for Subsequent Reads. // *************************** // Close the Channel and File. fc.close(); fin.close(); // ************************************** // Obtain the File as a Byte Array from // our input Buffer. if (buffer.hasArray()) { //byte[] xBytes = new byte[ filesize.intValue() ]; //xBytes = buffer.array(); return (buffer.array()); } else { return (_inrefurl.getBytes()); } // End of Else. // *********************************** // Any Exceptions, we shall // simple return the incoming // Reference URL. } catch (final MalformedURLException e) { return (_inrefurl.getBytes()); } catch (final IOException e) { return (_inrefurl.getBytes()); } // end of Exception clause. } // End of Private dereferenceValueWithNIO Method. /** * Private method for creating Attributes Object of * current LDIF entry being formulated. */ private boolean checkForVersion(String _inputline) { // ********************************************* // If this is our first line, just for a version // indication. if (First_Line) { First_Line = false; if ((_inputline.length() > 8) && ("version".equalsIgnoreCase(_inputline.substring(0, 7)))) { // **************************** // Got a Version Line // So obtain the Version. LDIFVersion = _inputline.substring(8); LDIFVersion = LDIFVersion.trim(); return (true); } // End of Inner If. } // End of First Line. return (false); } // End of Private checkForVersion Method. } ///:~ End of idxLDIFReader Class