/****************************************************************************** * The MIT License * Copyright (c) 2003 Novell Inc. www.novell.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *******************************************************************************/ // // Novell.Directory.Ldap.LdapAttribute.cs // // Author: // Sunil Kumar (Sunilk@novell.com) // // (C) 2003 Novell, Inc (http://www.novell.com) // using System; using ArrayEnumeration = Novell.Directory.Ldap.Utilclass.ArrayEnumeration; using Base64 = Novell.Directory.Ldap.Utilclass.Base64; namespace Novell.Directory.Ldap { /// The name and values of one attribute of a directory entry. /// /// LdapAttribute objects are used when searching for, adding, /// modifying, and deleting attributes from the directory. /// LdapAttributes are often used in conjunction with an /// {@link LdapAttributeSet} when retrieving or adding multiple /// attributes to an entry. /// /// /// /// /// /// /// /// /// /// public class LdapAttribute : System.ICloneable, System.IComparable { class URLData { private void InitBlock(LdapAttribute enclosingInstance) { this.enclosingInstance = enclosingInstance; } private LdapAttribute enclosingInstance; public LdapAttribute Enclosing_Instance { get { return enclosingInstance; } } private int length; private sbyte[] data; public URLData(LdapAttribute enclosingInstance, sbyte[] data, int length) { InitBlock(enclosingInstance); this.length = length; this.data = data; return ; } public int getLength() { return length; } public sbyte[] getData() { return data; } } /// Returns an enumerator for the values of the attribute in byte format. /// /// /// The values of the attribute in byte format. /// Note: All string values will be UTF-8 encoded. To decode use the /// String constructor. Example: new String( byteArray, "UTF-8" ); /// virtual public System.Collections.IEnumerator ByteValues { get { return new ArrayEnumeration(ByteValueArray); } } /// Returns an enumerator for the string values of an attribute. /// /// /// The string values of an attribute. /// virtual public System.Collections.IEnumerator StringValues { get { return new ArrayEnumeration(StringValueArray); } } /// Returns the values of the attribute as an array of bytes. /// /// /// The values as an array of bytes or an empty array if there are /// no values. /// [CLSCompliantAttribute(false)] virtual public sbyte[][] ByteValueArray { get { if (null == this.values) return new sbyte[0][]; int size = this.values.Length; sbyte[][] bva = new sbyte[size][]; // Deep copy so application cannot change values for (int i = 0, u = size; i < u; i++) { bva[i] = new sbyte[((sbyte[]) values[i]).Length]; Array.Copy((System.Array) this.values[i], 0, (System.Array) bva[i], 0, bva[i].Length); } return bva; } } /// Returns the values of the attribute as an array of strings. /// /// /// The values as an array of strings or an empty array if there are /// no values /// virtual public System.String[] StringValueArray { get { if (null == this.values) return new System.String[0]; int size = values.Length; System.String[] sva = new System.String[size]; for (int j = 0; j < size; j++) { try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); char[] dchar = encoder.GetChars(SupportClass.ToByteArray((sbyte[])values[j])); // char[] dchar = encoder.GetChars((byte[])values[j]); sva[j] = new String(dchar); // sva[j] = new String((sbyte[]) values[j], "UTF-8"); } catch (System.IO.IOException uee) { // Exception should NEVER get thrown but just in case it does ... throw new System.SystemException(uee.ToString()); } } return sva; } } /// Returns the the first value of the attribute as a String. /// /// /// The UTF-8 encodedString value of the attribute's /// value. If the value wasn't a UTF-8 encoded String /// to begin with the value of the returned String is /// non deterministic. /// /// If this attribute has more than one value the /// first value is converted to a UTF-8 encoded String /// and returned. It should be noted, that the directory may /// return attribute values in any order, so that the first /// value may vary from one call to another. /// /// If the attribute has no values null is returned /// virtual public System.String StringValue { get { System.String rval = null; if (this.values != null) { try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); char[] dchar = encoder.GetChars(SupportClass.ToByteArray((sbyte[])this.values[0])); // char[] dchar = encoder.GetChars((byte[]) this.values[0]); rval = new String(dchar); } catch (System.IO.IOException use) { throw new System.SystemException(use.ToString()); } } return rval; } } /// Returns the the first value of the attribute as a byte array. /// /// /// The binary value of this attribute or /// null if this attribute doesn't have a value. /// /// If the attribute has no values null is returned /// [CLSCompliantAttribute(false)] virtual public sbyte[] ByteValue { get { sbyte[] bva = null; if (this.values != null) { // Deep copy so app can't change the value bva = new sbyte[((sbyte[]) values[0]).Length]; Array.Copy((System.Array) this.values[0], 0, (System.Array) bva, 0, bva.Length); } return bva; } } /// Returns the language subtype of the attribute, if any. /// /// For example, if the attribute name is cn;lang-ja;phonetic, /// this method returns the string, lang-ja. /// /// /// The language subtype of the attribute or null if the attribute /// has none. /// virtual public System.String LangSubtype { get { if (subTypes != null) { for (int i = 0; i < subTypes.Length; i++) { if (subTypes[i].StartsWith("lang-")) { return subTypes[i]; } } } return null; } } /// Returns the name of the attribute. /// /// /// The name of the attribute. /// virtual public System.String Name { get { return name; } } /// Replaces all values with the specified value. This protected method is /// used by sub-classes of LdapSchemaElement because the value cannot be set /// with a contructor. /// virtual protected internal System.String Value { set { values = null; try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); byte[] ibytes = encoder.GetBytes(value); sbyte[] sbytes=SupportClass.ToSByteArray(ibytes); this.add(sbytes); } catch (System.IO.IOException ue) { throw new System.SystemException(ue.ToString()); } return ; } } private System.String name; // full attribute name private System.String baseName; // cn of cn;lang-ja;phonetic private System.String[] subTypes = null; // lang-ja of cn;lang-ja private System.Object[] values = null; // Array of byte[] attribute values /// Constructs an attribute with copies of all values of the input /// attribute. /// /// /// An LdapAttribute to use as a template. /// /// @throws IllegalArgumentException if attr is null /// public LdapAttribute(LdapAttribute attr) { if (attr == null) { throw new System.ArgumentException("LdapAttribute class cannot be null"); } // Do a deep copy of the LdapAttribute template this.name = attr.name; this.baseName = attr.baseName; if (null != attr.subTypes) { this.subTypes = new System.String[attr.subTypes.Length]; Array.Copy((System.Array) attr.subTypes, 0, (System.Array) this.subTypes, 0, this.subTypes.Length); } // OK to just copy attributes, as the app only sees a deep copy of them if (null != attr.values) { this.values = new System.Object[attr.values.Length]; Array.Copy((System.Array) attr.values, 0, (System.Array) this.values, 0, this.values.Length); } return ; } /// Constructs an attribute with no values. /// /// /// Name of the attribute. /// /// @throws IllegalArgumentException if attrName is null /// public LdapAttribute(System.String attrName) { if ((System.Object) attrName == null) { throw new System.ArgumentException("Attribute name cannot be null"); } this.name = attrName; this.baseName = LdapAttribute.getBaseName(attrName); this.subTypes = LdapAttribute.getSubtypes(attrName); return ; } /// Constructs an attribute with a byte-formatted value. /// /// /// Name of the attribute. /// /// Value of the attribute as raw bytes. /// /// Note: If attrBytes represents a string it should be UTF-8 encoded. /// /// @throws IllegalArgumentException if attrName or attrBytes is null /// [CLSCompliantAttribute(false)] public LdapAttribute(System.String attrName, sbyte[] attrBytes):this(attrName) { if (attrBytes == null) { throw new System.ArgumentException("Attribute value cannot be null"); } // Make our own copy of the byte array to prevent app from changing it sbyte[] tmp = new sbyte[attrBytes.Length]; Array.Copy((System.Array) attrBytes, 0, (System.Array)tmp, 0, attrBytes.Length); this.add(tmp); return ; } /// Constructs an attribute with a single string value. /// /// /// Name of the attribute. /// /// Value of the attribute as a string. /// /// @throws IllegalArgumentException if attrName or attrString is null /// public LdapAttribute(System.String attrName, System.String attrString):this(attrName) { if ((System.Object) attrString == null) { throw new System.ArgumentException("Attribute value cannot be null"); } try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); byte[] ibytes = encoder.GetBytes(attrString); sbyte[] sbytes=SupportClass.ToSByteArray(ibytes); this.add(sbytes); } catch (System.IO.IOException e) { throw new System.SystemException(e.ToString()); } return ; } /// Constructs an attribute with an array of string values. /// /// /// Name of the attribute. /// /// Array of values as strings. /// /// @throws IllegalArgumentException if attrName, attrStrings, or a member /// of attrStrings is null /// public LdapAttribute(System.String attrName, System.String[] attrStrings):this(attrName) { if (attrStrings == null) { throw new System.ArgumentException("Attribute values array cannot be null"); } for (int i = 0, u = attrStrings.Length; i < u; i++) { try { if ((System.Object) attrStrings[i] == null) { throw new System.ArgumentException("Attribute value " + "at array index " + i + " cannot be null"); } System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); byte[] ibytes = encoder.GetBytes(attrStrings[i]); sbyte[] sbytes=SupportClass.ToSByteArray(ibytes); this.add(sbytes); // this.add(attrStrings[i].getBytes("UTF-8")); } catch (System.IO.IOException e) { throw new System.SystemException(e.ToString()); } } return ; } /// Returns a clone of this LdapAttribute. /// /// /// clone of this LdapAttribute. /// public System.Object Clone() { try { System.Object newObj = base.MemberwiseClone(); if (values != null) { Array.Copy((System.Array) this.values, 0, (System.Array) ((LdapAttribute) newObj).values, 0, this.values.Length); } return newObj; } catch (System.Exception ce) { throw new System.SystemException("Internal error, cannot create clone"); } } /// Adds a string value to the attribute. /// /// /// Value of the attribute as a String. /// /// @throws IllegalArgumentException if attrString is null /// public virtual void addValue(System.String attrString) { if ((System.Object) attrString == null) { throw new System.ArgumentException("Attribute value cannot be null"); } try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); byte[] ibytes = encoder.GetBytes(attrString); sbyte[] sbytes=SupportClass.ToSByteArray(ibytes); this.add(sbytes); // this.add(attrString.getBytes("UTF-8")); } catch (System.IO.IOException ue) { throw new System.SystemException(ue.ToString()); } return ; } /// Adds a byte-formatted value to the attribute. /// /// /// Value of the attribute as raw bytes. /// /// Note: If attrBytes represents a string it should be UTF-8 encoded. /// /// @throws IllegalArgumentException if attrBytes is null /// [CLSCompliantAttribute(false)] public virtual void addValue(sbyte[] attrBytes) { if (attrBytes == null) { throw new System.ArgumentException("Attribute value cannot be null"); } this.add(attrBytes); return ; } /// Adds a base64 encoded value to the attribute. /// The value will be decoded and stored as bytes. String /// data encoded as a base64 value must be UTF-8 characters. /// /// /// The base64 value of the attribute as a String. /// /// @throws IllegalArgumentException if attrString is null /// public virtual void addBase64Value(System.String attrString) { if ((System.Object) attrString == null) { throw new System.ArgumentException("Attribute value cannot be null"); } this.add(Base64.decode(attrString)); return ; } /// Adds a base64 encoded value to the attribute. /// The value will be decoded and stored as bytes. Character /// data encoded as a base64 value must be UTF-8 characters. /// /// /// The base64 value of the attribute as a StringBuffer. /// /// The start index of base64 encoded part, inclusive. /// /// The end index of base encoded part, exclusive. /// /// @throws IllegalArgumentException if attrString is null /// public virtual void addBase64Value(System.Text.StringBuilder attrString, int start, int end) { if (attrString == null) { throw new System.ArgumentException("Attribute value cannot be null"); } this.add(Base64.decode(attrString, start, end)); return ; } /// Adds a base64 encoded value to the attribute. /// The value will be decoded and stored as bytes. Character /// data encoded as a base64 value must be UTF-8 characters. /// /// /// The base64 value of the attribute as an array of /// characters. /// /// @throws IllegalArgumentException if attrString is null /// public virtual void addBase64Value(char[] attrChars) { if (attrChars == null) { throw new System.ArgumentException("Attribute value cannot be null"); } this.add(Base64.decode(attrChars)); return ; } /// Adds a URL, indicating a file or other resource that contains /// the value of the attribute. /// /// /// String value of a URL pointing to the resource containing /// the value of the attribute. /// /// @throws IllegalArgumentException if url is null /// public virtual void addURLValue(System.String url) { if ((System.Object) url == null) { throw new System.ArgumentException("Attribute URL cannot be null"); } addURLValue(new System.Uri(url)); return ; } /// Adds a URL, indicating a file or other resource that contains /// the value of the attribute. /// /// /// A URL class pointing to the resource containing the value /// of the attribute. /// /// @throws IllegalArgumentException if url is null /// public virtual void addURLValue(System.Uri url) { // Class to encapsulate the data bytes and the length if (url == null) { throw new System.ArgumentException("Attribute URL cannot be null"); } try { // Get InputStream from the URL System.IO.Stream in_Renamed = System.Net.WebRequest.Create(url).GetResponse().GetResponseStream(); // Read the bytes into buffers and store the them in an arraylist System.Collections.ArrayList bufs = new System.Collections.ArrayList(); sbyte[] buf = new sbyte[4096]; int len, totalLength = 0; while ((len = SupportClass.ReadInput(in_Renamed, ref buf, 0, 4096)) != - 1) { bufs.Add(new URLData(this, buf, len)); buf = new sbyte[4096]; totalLength += len; } /* * Now that the length is known, allocate an array to hold all * the bytes of data and copy the data to that array, store * it in this LdapAttribute */ sbyte[] data = new sbyte[totalLength]; int offset = 0; // for (int i = 0; i < bufs.Count; i++) { URLData b = (URLData) bufs[i]; len = b.getLength(); Array.Copy((System.Array) b.getData(), 0, (System.Array) data, offset, len); offset += len; } this.add(data); } catch (System.IO.IOException ue) { throw new System.SystemException(ue.ToString()); } return ; } /// Returns the base name of the attribute. /// /// For example, if the attribute name is cn;lang-ja;phonetic, /// this method returns cn. /// /// /// The base name of the attribute. /// public virtual System.String getBaseName() { return baseName; } /// Returns the base name of the specified attribute name. /// /// For example, if the attribute name is cn;lang-ja;phonetic, /// this method returns cn. /// /// /// Name of the attribute from which to extract the /// base name. /// /// /// The base name of the attribute. /// /// @throws IllegalArgumentException if attrName is null /// public static System.String getBaseName(System.String attrName) { if ((System.Object) attrName == null) { throw new System.ArgumentException("Attribute name cannot be null"); } int idx = attrName.IndexOf((System.Char) ';'); if (- 1 == idx) { return attrName; } return attrName.Substring(0, (idx) - (0)); } /// Extracts the subtypes from the attribute name. /// /// For example, if the attribute name is cn;lang-ja;phonetic, /// this method returns an array containing lang-ja and phonetic. /// /// /// An array subtypes or null if the attribute has none. /// public virtual System.String[] getSubtypes() { return subTypes; } /// Extracts the subtypes from the specified attribute name. /// /// For example, if the attribute name is cn;lang-ja;phonetic, /// this method returns an array containing lang-ja and phonetic. /// /// /// Name of the attribute from which to extract /// the subtypes. /// /// /// An array subtypes or null if the attribute has none. /// /// @throws IllegalArgumentException if attrName is null /// public static System.String[] getSubtypes(System.String attrName) { if ((System.Object) attrName == null) { throw new System.ArgumentException("Attribute name cannot be null"); } SupportClass.Tokenizer st = new SupportClass.Tokenizer(attrName, ";"); System.String[] subTypes = null; int cnt = st.Count; if (cnt > 0) { st.NextToken(); // skip over basename subTypes = new System.String[cnt - 1]; int i = 0; while (st.HasMoreTokens()) { subTypes[i++] = st.NextToken(); } } return subTypes; } /// Reports if the attribute name contains the specified subtype. /// /// For example, if you check for the subtype lang-en and the /// attribute name is cn;lang-en, this method returns true. /// /// /// The single subtype to check for. /// /// /// True, if the attribute has the specified subtype; /// false, if it doesn't. /// /// @throws IllegalArgumentException if subtype is null /// public virtual bool hasSubtype(System.String subtype) { if ((System.Object) subtype == null) { throw new System.ArgumentException("subtype cannot be null"); } if (null != this.subTypes) { for (int i = 0; i < subTypes.Length; i++) { if (subTypes[i].ToUpper().Equals(subtype.ToUpper())) return true; } } return false; } /// Reports if the attribute name contains all the specified subtypes. /// /// For example, if you check for the subtypes lang-en and phonetic /// and if the attribute name is cn;lang-en;phonetic, this method /// returns true. If the attribute name is cn;phonetic or cn;lang-en, /// this method returns false. /// /// /// An array of subtypes to check for. /// /// /// True, if the attribute has all the specified subtypes; /// false, if it doesn't have all the subtypes. /// /// @throws IllegalArgumentException if subtypes is null or if array member /// is null. /// public virtual bool hasSubtypes(System.String[] subtypes) { if (subtypes == null) { throw new System.ArgumentException("subtypes cannot be null"); } for (int i = 0; i < subtypes.Length; i++) { for (int j = 0; j < subTypes.Length; j++) { if ((System.Object) subTypes[j] == null) { throw new System.ArgumentException("subtype " + "at array index " + i + " cannot be null"); } if (subTypes[j].ToUpper().Equals(subtypes[i].ToUpper())) { goto gotSubType; } } return false; gotSubType: ; } return true; } /// Removes a string value from the attribute. /// /// /// Value of the attribute as a string. /// /// Note: Removing a value which is not present in the attribute has /// no effect. /// /// @throws IllegalArgumentException if attrString is null /// public virtual void removeValue(System.String attrString) { if (null == (System.Object) attrString) { throw new System.ArgumentException("Attribute value cannot be null"); } try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); byte[] ibytes = encoder.GetBytes(attrString); sbyte[] sbytes=SupportClass.ToSByteArray(ibytes); this.removeValue(sbytes); // this.removeValue(attrString.getBytes("UTF-8")); } catch (System.IO.IOException uee) { // This should NEVER happen but just in case ... throw new System.SystemException(uee.ToString()); } return ; } /// Removes a byte-formatted value from the attribute. /// /// /// Value of the attribute as raw bytes. /// Note: If attrBytes represents a string it should be UTF-8 encoded. /// Example: String.getBytes("UTF-8"); /// /// Note: Removing a value which is not present in the attribute has /// no effect. /// /// @throws IllegalArgumentException if attrBytes is null /// [CLSCompliantAttribute(false)] public virtual void removeValue(sbyte[] attrBytes) { if (null == attrBytes) { throw new System.ArgumentException("Attribute value cannot be null"); } for (int i = 0; i < this.values.Length; i++) { if (equals(attrBytes, (sbyte[]) this.values[i])) { if (0 == i && 1 == this.values.Length) { // Optimize if first element of a single valued attr this.values = null; return ; } if (this.values.Length == 1) { this.values = null; } else { int moved = this.values.Length - i - 1; System.Object[] tmp = new System.Object[this.values.Length - 1]; if (i != 0) { Array.Copy((System.Array) values, 0, (System.Array) tmp, 0, i); } if (moved != 0) { Array.Copy((System.Array) values, i + 1, (System.Array) tmp, i, moved); } this.values = tmp; tmp = null; } break; } } return ; } /// Returns the number of values in the attribute. /// /// /// The number of values in the attribute. /// public virtual int size() { return null == this.values?0:this.values.Length; } /// Compares this object with the specified object for order. /// /// Ordering is determined by comparing attribute names (see /// {@link #getName() }) using the method compareTo() of the String class. /// /// /// /// The LdapAttribute to be compared to this object. /// /// /// Returns a negative integer, zero, or a positive /// integer as this object is less than, equal to, or greater than the /// specified object. /// public virtual int CompareTo(System.Object attribute) { return name.CompareTo(((LdapAttribute) attribute).name); } /// Adds an object to this object's list of attribute values /// /// /// Ultimately all of this attribute's values are treated /// as binary data so we simplify the process by requiring /// that all data added to our list is in binary form. /// /// Note: If attrBytes represents a string it should be UTF-8 encoded. /// private void add(sbyte[] bytes) { if (null == this.values) { this.values = new System.Object[]{bytes}; } else { // Duplicate attribute values not allowed for (int i = 0; i < this.values.Length; i++) { if (equals(bytes, (sbyte[]) this.values[i])) { return ; // Duplicate, don't add } } System.Object[] tmp = new System.Object[this.values.Length + 1]; Array.Copy((System.Array) this.values, 0, (System.Array) tmp, 0, this.values.Length); tmp[this.values.Length] = bytes; this.values = tmp; tmp = null; } return ; } /// Returns true if the two specified arrays of bytes are equal to each /// another. Matches the logic of Arrays.equals which is not available /// in jdk 1.1.x. /// /// /// the first array to be tested /// /// the second array to be tested /// /// true if the two arrays are equal /// private bool equals(sbyte[] e1, sbyte[] e2) { // If same object, they compare true if (e1 == e2) return true; // If either but not both are null, they compare false if (e1 == null || e2 == null) return false; // If arrays have different length, they compare false int length = e1.Length; if (e2.Length != length) return false; // If any of the bytes are different, they compare false for (int i = 0; i < length; i++) { if (e1[i] != e2[i]) return false; } return true; } /// Returns a string representation of this LdapAttribute /// /// /// a string representation of this LdapAttribute /// public override System.String ToString() { System.Text.StringBuilder result = new System.Text.StringBuilder("LdapAttribute: "); try { result.Append("{type='" + name + "'"); if (values != null) { result.Append(", "); if (values.Length == 1) { result.Append("value='"); } else { result.Append("values='"); } for (int i = 0; i < values.Length; i++) { if (i != 0) { result.Append("','"); } if (((sbyte[]) values[i]).Length == 0) { continue; } System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); // char[] dchar = encoder.GetChars((byte[]) values[i]); char[] dchar = encoder.GetChars(SupportClass.ToByteArray((sbyte[])values[i])); System.String sval = new String(dchar); // System.String sval = new String((sbyte[]) values[i], "UTF-8"); if (sval.Length == 0) { // didn't decode well, must be binary result.Append("