/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.tools.ldapdecoder.protocol; import com.unboundid.util.Base64; import com.slamd.asn1.ASN1Element; import com.slamd.asn1.ASN1OctetString; import com.slamd.asn1.ASN1Sequence; import com.slamd.asn1.ASN1Set; /** * This class defines an LDAP attribute, which has a type and zero or more * values. * * * @author Neil A. Wilson */ public class LDAPAttribute { // The set of values associated with this attribute. private ASN1OctetString[] values; // The type for this attribute. private String type; /** * Creates a new LDAP attribute with the provided type and values. * * @param type The attribute type for this attribute. * @param values The set of values to include with this attribute. */ public LDAPAttribute(String type, ASN1OctetString[] values) { this.type = type; this.values = values; } /** * Retrieves the attribute type for this attribute. * * @return The attribute type for this attribute. */ public String getType() { return type; } /** * Retrieves the set of values for this attribute. * * @return The set of values for this attribute. */ public ASN1OctetString[] getValues() { return values; } /** * Encodes this LDAP attribute to an ASN.1 element. * * @return The ASN.1 element containing the encoded attribute. */ public ASN1Element encode() { ASN1Set valueSet; if ((values == null) || (values.length == 0)) { valueSet = new ASN1Set(); } else { valueSet = new ASN1Set(values); } ASN1Element[] attrElements = new ASN1Element[] { new ASN1OctetString(type), valueSet }; return new ASN1Sequence(attrElements); } /** * Decodes the provided ASN.1 element as an LDAP attribute. * * @param element The ASN.1 element to decode as an LDAP attribute. * * @return The decoded attribute. * * @throws ProtocolException If a problem occurs while decoding the provided * ASN.1 element as an LDAP attribute. */ public static LDAPAttribute decode(ASN1Element element) throws ProtocolException { ASN1Element[] attrElements; try { attrElements = element.decodeAsSequence().getElements(); } catch (Exception e) { throw new ProtocolException("Unable to decode attribute sequence", e); } if (attrElements.length != 2) { throw new ProtocolException("There must be exactly two elements in an " + "attribute sequence"); } String type; try { type = attrElements[0].decodeAsOctetString().getStringValue(); } catch (Exception e) { throw new ProtocolException("Unable to decode attribute type", e); } ASN1OctetString[] values; try { ASN1Element[] valueElements = attrElements[1].decodeAsSet().getElements(); values = new ASN1OctetString[valueElements.length]; for (int i=0; i < values.length; i++) { values[i] = valueElements[i].decodeAsOctetString(); } } catch (Exception e) { throw new ProtocolException("Unable to decode attribute value set", e); } return new LDAPAttribute(type, values); } /** * Indicates whether the provided attribute value should be base64-encoded * when represented in LDIF form as per RFC 2849. * * @param value The value for which to make the determination. * * @return <CODE>true</CODE> if the value should be base64-encoded in LDIF * form, or <CODE>false</CODE> if that is not necessary. */ public static boolean valueNeedsBase64Encoding(ASN1OctetString value) { byte[] valueBytes = value.getValue(); if (valueBytes.length == 0) { return false; } // Compare the first byte against the SAFE-INIT-CHAR list. byte firstByte = valueBytes[0]; if (((firstByte & 0x7F) != firstByte) || // Anything > 127 (firstByte == 0x00) || // NUL (firstByte == 0x0A) || // LF (firstByte == 0x0D) || // CR (firstByte == 0x20) || // SPACE (firstByte == 0x3A) || // colon (firstByte == 0x3C)) // less-than { return true; } // Compare the remaining bytes against the SAFE-CHAR list. for (int i=1; i < valueBytes.length; i++) { if (((valueBytes[i] & 0x7F) != valueBytes[i]) || // Anything > 127 (valueBytes[i] == 0x00) || // NUL (valueBytes[i] == 0x0A) || // LF (valueBytes[i] == 0x0D)) // CR { return true; } } // We haven't found a reason to encode it, so return false. return false; } /** * Retrieves a string representation of this LDAP attribute. * * @return A string representation of this LDAP attribute. */ public String toString() { return toString(0); } /** * Retrieves a string representation of this LDAP attribute with the specified * indent. * * @param indent The number of spaces to the left of the value. * * @return A string representation of this LDAP attribute. */ public String toString(int indent) { StringBuilder indentBuf = new StringBuilder(indent); for (int i=0; i < indent; i++) { indentBuf.append(' '); } StringBuilder buffer = new StringBuilder(); if ((values == null) || (values.length == 0)) { buffer.append(indentBuf).append(type).append(": "). append(LDAPMessage.EOL); } else { for (int i=0; i < values.length; i++) { StringBuilder attrBuf = new StringBuilder(); attrBuf.append(type); if (valueNeedsBase64Encoding(values[i])) { attrBuf.append(":: "). append(Base64.encode(values[i].getValue())); } else { attrBuf.append(": ").append(values[i].getStringValue()); } if (attrBuf.length() > 75) { buffer.append(indentBuf).append(attrBuf.substring(0, 75)). append(LDAPMessage.EOL); int startPos = 75; int endPos = Math.min(startPos+74, attrBuf.length()); while (startPos < attrBuf.length()) { buffer.append(indentBuf).append(' '). append(attrBuf.substring(startPos, endPos)). append(LDAPMessage.EOL); startPos += 74; endPos = Math.min(startPos+74, attrBuf.length()); } } else { buffer.append(indentBuf).append(attrBuf).append(LDAPMessage.EOL); } } } return buffer.toString(); } }