/* * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap; import javax.naming.*; import javax.naming.directory.*; import java.util.Hashtable; import java.util.Vector; /** * Netscape's 3.1 servers have some schema bugs: * - It puts quotes around OIDs (such as those for SUP, SYNTAX). * - When you try to write out the MUST/MAY list (such as "MUST cn"), * it wants ("MUST (cn)") instead */ final class LdapSchemaParser { // do debugging private static final boolean debug = false; // names of attribute IDs in the LDAP schema entry static final String OBJECTCLASSDESC_ATTR_ID = "objectClasses"; static final String ATTRIBUTEDESC_ATTR_ID = "attributeTypes"; static final String SYNTAXDESC_ATTR_ID = "ldapSyntaxes"; static final String MATCHRULEDESC_ATTR_ID = "matchingRules"; // information for creating internal nodes in JNDI schema tree static final String OBJECTCLASS_DEFINITION_NAME = "ClassDefinition"; private static final String[] CLASS_DEF_ATTRS = { "objectclass", "ClassDefinition"}; static final String ATTRIBUTE_DEFINITION_NAME = "AttributeDefinition"; private static final String[] ATTR_DEF_ATTRS = { "objectclass", "AttributeDefinition" }; static final String SYNTAX_DEFINITION_NAME = "SyntaxDefinition"; private static final String[] SYNTAX_DEF_ATTRS = { "objectclass", "SyntaxDefinition" }; static final String MATCHRULE_DEFINITION_NAME = "MatchingRule"; private static final String[] MATCHRULE_DEF_ATTRS = { "objectclass", "MatchingRule" }; // special tokens used in LDAP schema descriptions private static final char SINGLE_QUOTE = '\''; private static final char WHSP = ' '; private static final char OID_LIST_BEGIN = '('; private static final char OID_LIST_END = ')'; private static final char OID_SEPARATOR = '$'; // common IDs private static final String NUMERICOID_ID = "NUMERICOID"; private static final String NAME_ID = "NAME"; private static final String DESC_ID = "DESC"; private static final String OBSOLETE_ID = "OBSOLETE"; private static final String SUP_ID = "SUP"; private static final String PRIVATE_ID = "X-"; // Object Class specific IDs private static final String ABSTRACT_ID = "ABSTRACT"; private static final String STRUCTURAL_ID = "STRUCTURAL"; private static final String AUXILARY_ID = "AUXILIARY"; private static final String MUST_ID = "MUST"; private static final String MAY_ID = "MAY"; // Attribute Type specific IDs private static final String EQUALITY_ID = "EQUALITY"; private static final String ORDERING_ID = "ORDERING"; private static final String SUBSTR_ID = "SUBSTR"; private static final String SYNTAX_ID = "SYNTAX"; private static final String SINGLE_VAL_ID = "SINGLE-VALUE"; private static final String COLLECTIVE_ID = "COLLECTIVE"; private static final String NO_USER_MOD_ID = "NO-USER-MODIFICATION"; private static final String USAGE_ID = "USAGE"; // The string value we give to boolean variables private static final String SCHEMA_TRUE_VALUE = "true"; // To get around writing schemas that crash Netscape server private boolean netscapeBug; LdapSchemaParser(boolean netscapeBug) { this.netscapeBug = netscapeBug; } final static void LDAP2JNDISchema(Attributes schemaAttrs, LdapSchemaCtx schemaRoot) throws NamingException { Attribute objectClassesAttr = null; Attribute attributeDefAttr = null; Attribute syntaxDefAttr = null; Attribute matchRuleDefAttr = null; objectClassesAttr = schemaAttrs.get(OBJECTCLASSDESC_ATTR_ID); if(objectClassesAttr != null) { objectDescs2ClassDefs(objectClassesAttr,schemaRoot); } attributeDefAttr = schemaAttrs.get(ATTRIBUTEDESC_ATTR_ID); if(attributeDefAttr != null) { attrDescs2AttrDefs(attributeDefAttr, schemaRoot); } syntaxDefAttr = schemaAttrs.get(SYNTAXDESC_ATTR_ID); if(syntaxDefAttr != null) { syntaxDescs2SyntaxDefs(syntaxDefAttr, schemaRoot); } matchRuleDefAttr = schemaAttrs.get(MATCHRULEDESC_ATTR_ID); if(matchRuleDefAttr != null) { matchRuleDescs2MatchRuleDefs(matchRuleDefAttr, schemaRoot); } } final private static DirContext objectDescs2ClassDefs(Attribute objDescsAttr, LdapSchemaCtx schemaRoot) throws NamingException { NamingEnumeration objDescs; Attributes objDef; LdapSchemaCtx classDefTree; // create the class def subtree Attributes attrs = new BasicAttributes(LdapClient.caseIgnore); attrs.put(CLASS_DEF_ATTRS[0], CLASS_DEF_ATTRS[1]); classDefTree = schemaRoot.setup(LdapSchemaCtx.OBJECTCLASS_ROOT, OBJECTCLASS_DEFINITION_NAME, attrs); objDescs = objDescsAttr.getAll(); String currentName; while(objDescs.hasMore()) { String objDesc = (String)objDescs.next(); try { Object[] def = desc2Def(objDesc); currentName = (String) def[0]; objDef = (Attributes) def[1]; classDefTree.setup(LdapSchemaCtx.OBJECTCLASS, currentName, objDef); } catch (NamingException ne) { // error occurred while parsing, ignore current entry } } return classDefTree; } final private static DirContext attrDescs2AttrDefs(Attribute attributeDescAttr, LdapSchemaCtx schemaRoot) throws NamingException { NamingEnumeration attrDescs; Attributes attrDef; LdapSchemaCtx attrDefTree; // create the AttributeDef subtree Attributes attrs = new BasicAttributes(LdapClient.caseIgnore); attrs.put(ATTR_DEF_ATTRS[0], ATTR_DEF_ATTRS[1]); attrDefTree = schemaRoot.setup(LdapSchemaCtx.ATTRIBUTE_ROOT, ATTRIBUTE_DEFINITION_NAME, attrs); attrDescs = attributeDescAttr.getAll(); String currentName; while(attrDescs.hasMore()) { String attrDesc = (String)attrDescs.next(); try { Object[] def = desc2Def(attrDesc); currentName = (String) def[0]; attrDef = (Attributes) def[1]; attrDefTree.setup(LdapSchemaCtx.ATTRIBUTE, currentName, attrDef); } catch (NamingException ne) { // error occurred while parsing, ignore current entry } } return attrDefTree; } final private static DirContext syntaxDescs2SyntaxDefs( Attribute syntaxDescAttr, LdapSchemaCtx schemaRoot) throws NamingException { NamingEnumeration syntaxDescs; Attributes syntaxDef; LdapSchemaCtx syntaxDefTree; // create the SyntaxDef subtree Attributes attrs = new BasicAttributes(LdapClient.caseIgnore); attrs.put(SYNTAX_DEF_ATTRS[0], SYNTAX_DEF_ATTRS[1]); syntaxDefTree = schemaRoot.setup(LdapSchemaCtx.SYNTAX_ROOT, SYNTAX_DEFINITION_NAME, attrs); syntaxDescs = syntaxDescAttr.getAll(); String currentName; while(syntaxDescs.hasMore()) { String syntaxDesc = (String)syntaxDescs.next(); try { Object[] def = desc2Def(syntaxDesc); currentName = (String) def[0]; syntaxDef = (Attributes) def[1]; syntaxDefTree.setup(LdapSchemaCtx.SYNTAX, currentName, syntaxDef); } catch (NamingException ne) { // error occurred while parsing, ignore current entry } } return syntaxDefTree; } final private static DirContext matchRuleDescs2MatchRuleDefs( Attribute matchRuleDescAttr, LdapSchemaCtx schemaRoot) throws NamingException { NamingEnumeration matchRuleDescs; Attributes matchRuleDef; LdapSchemaCtx matchRuleDefTree; // create the MatchRuleDef subtree Attributes attrs = new BasicAttributes(LdapClient.caseIgnore); attrs.put(MATCHRULE_DEF_ATTRS[0], MATCHRULE_DEF_ATTRS[1]); matchRuleDefTree = schemaRoot.setup(LdapSchemaCtx.MATCHRULE_ROOT, MATCHRULE_DEFINITION_NAME, attrs); matchRuleDescs = matchRuleDescAttr.getAll(); String currentName; while(matchRuleDescs.hasMore()) { String matchRuleDesc = (String)matchRuleDescs.next(); try { Object[] def = desc2Def(matchRuleDesc); currentName = (String) def[0]; matchRuleDef = (Attributes) def[1]; matchRuleDefTree.setup(LdapSchemaCtx.MATCHRULE, currentName, matchRuleDef); } catch (NamingException ne) { // error occurred while parsing, ignore current entry } } return matchRuleDefTree; } final private static Object[] desc2Def(String desc) throws NamingException { //System.err.println(desc); Attributes attrs = new BasicAttributes(LdapClient.caseIgnore); Attribute attr = null; int[] pos = new int[]{1}; // tolerate missing leading space boolean moreTags = true; // Always begins with <whsp numericoid whsp> attr = readNumericOID(desc, pos); String currentName = (String) attr.get(0); // name is OID by default attrs.put(attr); skipWhitespace(desc, pos); while (moreTags) { attr = readNextTag(desc, pos); attrs.put(attr); if (attr.getID().equals(NAME_ID)) { currentName = (String) attr.get(0); // use NAME attribute as name } skipWhitespace(desc, pos); if( pos[0] >= desc.length() -1 ) { moreTags = false; } } return new Object[] {currentName, attrs}; } // returns the index of the first whitespace char of a linear whitspace // sequince ending at the given position. final private static int findTrailingWhitespace(String string, int pos) { for(int i = pos; i > 0; i--) { if(string.charAt(i) != WHSP) { return i + 1; } } return 0; } final private static void skipWhitespace(String string, int[] pos) { for(int i=pos[0]; i < string.length(); i++) { if(string.charAt(i) != WHSP) { pos[0] = i; if (debug) { System.err.println("skipWhitespace: skipping to "+i); } return; } } } final private static Attribute readNumericOID(String string, int[] pos) throws NamingException { if (debug) { System.err.println("readNumericoid: pos="+pos[0]); } int begin, end; String value = null; skipWhitespace(string, pos); begin = pos[0]; end = string.indexOf(WHSP, begin); if (end == -1 || end - begin < 1) { throw new InvalidAttributeValueException("no numericoid found: " + string); } value = string.substring(begin, end); pos[0] += value.length(); return new BasicAttribute(NUMERICOID_ID, value); } final private static Attribute readNextTag(String string, int[] pos) throws NamingException { Attribute attr = null; String tagName = null; String[] values = null; skipWhitespace(string, pos); if (debug) { System.err.println("readNextTag: pos="+pos[0]); } // get the name and values of the attribute to return int trailingSpace = string.indexOf( WHSP, pos[0] ); // tolerate a schema that omits the trailing space if (trailingSpace < 0) { tagName = string.substring( pos[0], string.length() - 1); } else { tagName = string.substring( pos[0], trailingSpace ); } values = readTag(tagName, string, pos); // make sure at least one value was returned if(values.length < 0) { throw new InvalidAttributeValueException("no values for " + "attribute \"" + tagName + "\""); } // create the attribute, using the first value attr = new BasicAttribute(tagName, values[0]); // add other values if there are any for(int i = 1; i < values.length; i++) { attr.add(values[i]); } return attr; } final private static String[] readTag(String tag, String string, int[] pos) throws NamingException { if (debug) { System.err.println("ReadTag: " + tag + " pos="+pos[0]); } // move parser past tag name pos[0] += tag.length(); skipWhitespace(string, pos); if (tag.equals(NAME_ID)) { return readQDescrs(string, pos); // names[0] is NAME } if(tag.equals(DESC_ID)) { return readQDString(string, pos); } if ( tag.equals(EQUALITY_ID) || tag.equals(ORDERING_ID) || tag.equals(SUBSTR_ID) || tag.equals(SYNTAX_ID)) { return readWOID(string, pos); } if (tag.equals(OBSOLETE_ID) || tag.equals(ABSTRACT_ID) || tag.equals(STRUCTURAL_ID) || tag.equals(AUXILARY_ID) || tag.equals(SINGLE_VAL_ID) || tag.equals(COLLECTIVE_ID) || tag.equals(NO_USER_MOD_ID)) { return new String[] {SCHEMA_TRUE_VALUE}; } if (tag.equals(SUP_ID) || // oid list for object class; WOID for attribute tag.equals(MUST_ID) || tag.equals(MAY_ID) || tag.equals(USAGE_ID)) { return readOIDs(string, pos); } // otherwise it's a schema element with a quoted string value return readQDStrings(string, pos); } final private static String[] readQDString(String string, int[] pos) throws NamingException { int begin, end; begin = string.indexOf(SINGLE_QUOTE, pos[0]) + 1; end = string.indexOf(SINGLE_QUOTE, begin); if (debug) { System.err.println("ReadQDString: pos=" + pos[0] + " begin=" + begin + " end=" + end); } if(begin == -1 || end == -1 || begin == end) { throw new InvalidAttributeIdentifierException("malformed " + "QDString: " + string); } // make sure the qdstring end symbol is there if (string.charAt(begin - 1) != SINGLE_QUOTE) { throw new InvalidAttributeIdentifierException("qdstring has " + "no end mark: " + string); } pos[0] = end+1; return new String[] {string.substring(begin, end)}; } /** * dstring = 1*utf8 * qdstring = whsp "'" dstring "'" whsp * qdstringlist = [ qdstring *( qdstring ) ] * qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp ) */ private final static String[] readQDStrings(String string, int[] pos) throws NamingException { return readQDescrs(string, pos); } /** * ; object descriptors used as schema element names * qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp ) * qdescrlist = [ qdescr *( qdescr ) ] * qdescr = whsp "'" descr "'" whsp * descr = keystring */ final private static String[] readQDescrs(String string, int[] pos) throws NamingException { if (debug) { System.err.println("readQDescrs: pos="+pos[0]); } skipWhitespace(string, pos); switch( string.charAt(pos[0]) ) { case OID_LIST_BEGIN: return readQDescrList(string, pos); case SINGLE_QUOTE: return readQDString(string, pos); default: throw new InvalidAttributeValueException("unexpected oids " + "string: " + string); } } /** * qdescrlist = [ qdescr *( qdescr ) ] * qdescr = whsp "'" descr "'" whsp * descr = keystring */ final private static String[] readQDescrList(String string, int[] pos) throws NamingException { int begin, end; Vector values = new Vector(5); if (debug) { System.err.println("ReadQDescrList: pos="+pos[0]); } pos[0]++; // skip '(' skipWhitespace(string, pos); begin = pos[0]; end = string.indexOf(OID_LIST_END, begin); if(end == -1) { throw new InvalidAttributeValueException ("oidlist has no end "+ "mark: " + string); } while(begin < end) { String[] one = readQDString(string, pos); if (debug) { System.err.println("ReadQDescrList: found '" + one[0] + "' at begin=" + begin + " end =" + end); } values.addElement(one[0]); skipWhitespace(string, pos); begin = pos[0]; } pos[0] = end+1; // skip ')' String[] answer = new String[values.size()]; for (int i = 0; i < answer.length; i++) { answer[i] = (String)values.elementAt(i); } return answer; } final private static String[] readWOID(String string, int[] pos) throws NamingException { if (debug) { System.err.println("readWOIDs: pos="+pos[0]); } skipWhitespace(string, pos); if (string.charAt(pos[0]) == SINGLE_QUOTE) { // %%% workaround for Netscape schema bug return readQDString(string, pos); } int begin, end; begin = pos[0]; end = string.indexOf(WHSP, begin); if (debug) { System.err.println("ReadWOID: pos=" + pos[0] + " begin=" + begin + " end=" + end); } if(end == -1 || begin == end) { throw new InvalidAttributeIdentifierException("malformed " + "OID: " + string); } pos[0] = end+1; return new String[] {string.substring(begin, end)}; } /* * oids = woid / ( "(" oidlist ")" ) * oidlist = woid *( "$" woid ) */ final private static String[] readOIDs(String string, int[] pos) throws NamingException { if (debug) { System.err.println("readOIDs: pos="+pos[0]); } skipWhitespace(string, pos); // Single OID if (string.charAt(pos[0]) != OID_LIST_BEGIN) { return readWOID(string, pos); } // Multiple OIDs int begin, cur, end; String oidName = null; Vector values = new Vector(5); if (debug) { System.err.println("ReadOIDList: pos="+pos[0]); } pos[0]++; skipWhitespace(string, pos); begin = pos[0]; end = string.indexOf(OID_LIST_END, begin); cur = string.indexOf(OID_SEPARATOR, begin); if(end == -1) { throw new InvalidAttributeValueException ("oidlist has no end "+ "mark: " + string); } if(cur == -1 || end < cur) { cur = end; } while(cur < end && cur > 0) { int wsBegin = findTrailingWhitespace(string, cur - 1); oidName = string.substring(begin, wsBegin); if (debug) { System.err.println("ReadOIDList: found '" + oidName + "' at begin=" + begin + " end =" + end); } values.addElement(oidName); pos[0] = cur + 1; skipWhitespace(string, pos); begin = pos[0]; cur = string.indexOf(OID_SEPARATOR, begin); if(debug) {System.err.println("ReadOIDList: begin = " + begin);} } if (debug) { System.err.println("ReadOIDList: found '" + oidName + "' at begin=" + begin + " end =" + end); } int wsBegin = findTrailingWhitespace(string, end - 1); oidName = string.substring(begin, wsBegin); values.addElement(oidName); pos[0] = end+1; String[] answer = new String[values.size()]; for (int i = 0; i < answer.length; i++) { answer[i] = (String)values.elementAt(i); } return answer; } // ----------------- "unparser" methods // Methods that are used for translating a node in the schema tree // into RFC2252 format for storage back into the LDAP directory /* static Attributes JNDI2LDAPSchema(DirContext schemaRoot) throws NamingException { Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID); Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID); Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID); Attributes attrs = new BasicAttributes(LdapClient.caseIgnore); DirContext classDefs, attributeDefs, syntaxDefs; Attributes classDefsAttrs, attributeDefsAttrs, syntaxDefsAttrs; NamingEnumeration defs; Object obj; int i = 0; try { obj = schemaRoot.lookup(OBJECTCLASS_DEFINITION_NAME); if(obj != null && obj instanceof DirContext) { classDefs = (DirContext)obj; defs = classDefs.listBindings(""); while(defs.hasMoreElements()) { i++; DirContext classDef = (DirContext) ((Binding)(defs.next())).getObject(); classDefAttrs = classDef.getAttributes(""); objDescAttr.add(classDef2ObjectDesc(classDefAttrs)); } if (debug) System.err.println(i + " total object classes"); attrs.put(objDescAttr); } else { throw new NamingException( "Problem with Schema tree: the object named " + OBJECTCLASS_DEFINITION_NAME + " is not a " + "DirContext"); } } catch (NameNotFoundException e) {} // ignore i=0; try { obj = schemaRoot.lookup(ATTRIBUTE_DEFINITION_NAME); if(obj instanceof DirContext) { attributeDefs = (DirContext)obj; defs = attributeDefs.listBindings(""); while(defs.hasMoreElements()) { i++; DirContext attrDef = (DirContext) ((Binding)defs.next()).getObject(); attrDefAttrs = attrDef.getAttributes(""); attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs)); } if (debug) System.err.println(i + " attribute definitions"); attrs.put(attrDescAttr); } else { throw new NamingException( "Problem with schema tree: the object named " + ATTRIBUTE_DEFINITION_NAME + " is not a " + "DirContext"); } } catch (NameNotFoundException e) {} // ignore i=0; try { obj = schemaRoot.lookup(SYNTAX_DEFINITION_NAME); if(obj instanceof DirContext) { syntaxDefs = (DirContext)obj; defs =syntaxDefs.listBindings(""); while(defs.hasMoreElements()) { i++; DirContext syntaxDef = (DirContext) ((Binding)defs.next()).getObject(); syntaxDefAttrs = syntaxDef.getAttributes(""); syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs)); } if (debug) System.err.println(i + " total syntax definitions"); attrs.put(syntaxDescAttr); } else { throw new NamingException( "Problem with schema tree: the object named " + SYNTAX_DEFINITION_NAME + " is not a " + "DirContext"); } } catch (NameNotFoundException e) {} // ignore return attrs; } */ /** * Translate attributes that describe an object class into the * string description as defined in RFC 2252. */ final private String classDef2ObjectDesc(Attributes attrs) throws NamingException { StringBuffer objectDesc = new StringBuffer("( "); Attribute attr = null; int count = 0; // extract attributes by ID to guarantee ordering attr = attrs.get(NUMERICOID_ID); if (attr != null) { objectDesc.append(writeNumericOID(attr)); count++; } else { throw new ConfigurationException("Class definition doesn't" + "have a numeric OID"); } attr = attrs.get(NAME_ID); if (attr != null) { objectDesc.append(writeQDescrs(attr)); count++; } attr = attrs.get(DESC_ID); if (attr != null) { objectDesc.append(writeQDString(attr)); count++; } attr = attrs.get(OBSOLETE_ID); if (attr != null) { objectDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(SUP_ID); if (attr != null) { objectDesc.append(writeOIDs(attr)); count++; } attr = attrs.get(ABSTRACT_ID); if (attr != null) { objectDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(STRUCTURAL_ID); if (attr != null) { objectDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(AUXILARY_ID); if (attr != null) { objectDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(MUST_ID); if (attr != null) { objectDesc.append(writeOIDs(attr)); count++; } attr = attrs.get(MAY_ID); if (attr != null) { objectDesc.append(writeOIDs(attr)); count++; } // process any remaining attributes if (count < attrs.size()) { String attrId = null; // use enumeration because attribute ID is not known for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) { attr = (Attribute)ae.next(); attrId = attr.getID(); // skip those already processed if (attrId.equals(NUMERICOID_ID) || attrId.equals(NAME_ID) || attrId.equals(SUP_ID) || attrId.equals(MAY_ID) || attrId.equals(MUST_ID) || attrId.equals(STRUCTURAL_ID) || attrId.equals(DESC_ID) || attrId.equals(AUXILARY_ID) || attrId.equals(ABSTRACT_ID) || attrId.equals(OBSOLETE_ID)) { continue; } else { objectDesc.append(writeQDStrings(attr)); } } } objectDesc.append(")"); return objectDesc.toString(); } /** * Translate attributes that describe an attribute definition into the * string description as defined in RFC 2252. */ final private String attrDef2AttrDesc(Attributes attrs) throws NamingException { StringBuffer attrDesc = new StringBuffer("( "); // opening parens Attribute attr = null; int count = 0; // extract attributes by ID to guarantee ordering attr = attrs.get(NUMERICOID_ID); if (attr != null) { attrDesc.append(writeNumericOID(attr)); count++; } else { throw new ConfigurationException("Attribute type doesn't" + "have a numeric OID"); } attr = attrs.get(NAME_ID); if (attr != null) { attrDesc.append(writeQDescrs(attr)); count++; } attr = attrs.get(DESC_ID); if (attr != null) { attrDesc.append(writeQDString(attr)); count++; } attr = attrs.get(OBSOLETE_ID); if (attr != null) { attrDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(SUP_ID); if (attr != null) { attrDesc.append(writeWOID(attr)); count++; } attr = attrs.get(EQUALITY_ID); if (attr != null) { attrDesc.append(writeWOID(attr)); count++; } attr = attrs.get(ORDERING_ID); if (attr != null) { attrDesc.append(writeWOID(attr)); count++; } attr = attrs.get(SUBSTR_ID); if (attr != null) { attrDesc.append(writeWOID(attr)); count++; } attr = attrs.get(SYNTAX_ID); if (attr != null) { attrDesc.append(writeWOID(attr)); count++; } attr = attrs.get(SINGLE_VAL_ID); if (attr != null) { attrDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(COLLECTIVE_ID); if (attr != null) { attrDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(NO_USER_MOD_ID); if (attr != null) { attrDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(USAGE_ID); if (attr != null) { attrDesc.append(writeQDString(attr)); count++; } // process any remaining attributes if (count < attrs.size()) { String attrId = null; // use enumeration because attribute ID is not known for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) { attr = (Attribute)ae.next(); attrId = attr.getID(); // skip those already processed if (attrId.equals(NUMERICOID_ID) || attrId.equals(NAME_ID) || attrId.equals(SYNTAX_ID) || attrId.equals(DESC_ID) || attrId.equals(SINGLE_VAL_ID) || attrId.equals(EQUALITY_ID) || attrId.equals(ORDERING_ID) || attrId.equals(SUBSTR_ID) || attrId.equals(NO_USER_MOD_ID) || attrId.equals(USAGE_ID) || attrId.equals(SUP_ID) || attrId.equals(COLLECTIVE_ID) || attrId.equals(OBSOLETE_ID)) { continue; } else { attrDesc.append(writeQDStrings(attr)); } } } attrDesc.append(")"); // add closing parens return attrDesc.toString(); } /** * Translate attributes that describe an attribute syntax definition into the * string description as defined in RFC 2252. */ final private String syntaxDef2SyntaxDesc(Attributes attrs) throws NamingException { StringBuffer syntaxDesc = new StringBuffer("( "); // opening parens Attribute attr = null; int count = 0; // extract attributes by ID to guarantee ordering attr = attrs.get(NUMERICOID_ID); if (attr != null) { syntaxDesc.append(writeNumericOID(attr)); count++; } else { throw new ConfigurationException("Attribute type doesn't" + "have a numeric OID"); } attr = attrs.get(DESC_ID); if (attr != null) { syntaxDesc.append(writeQDString(attr)); count++; } // process any remaining attributes if (count < attrs.size()) { String attrId = null; // use enumeration because attribute ID is not known for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) { attr = (Attribute)ae.next(); attrId = attr.getID(); // skip those already processed if (attrId.equals(NUMERICOID_ID) || attrId.equals(DESC_ID)) { continue; } else { syntaxDesc.append(writeQDStrings(attr)); } } } syntaxDesc.append(")"); return syntaxDesc.toString(); } /** * Translate attributes that describe an attribute matching rule * definition into the string description as defined in RFC 2252. */ final private String matchRuleDef2MatchRuleDesc(Attributes attrs) throws NamingException { StringBuffer matchRuleDesc = new StringBuffer("( "); // opening parens Attribute attr = null; int count = 0; // extract attributes by ID to guarantee ordering attr = attrs.get(NUMERICOID_ID); if (attr != null) { matchRuleDesc.append(writeNumericOID(attr)); count++; } else { throw new ConfigurationException("Attribute type doesn't" + "have a numeric OID"); } attr = attrs.get(NAME_ID); if (attr != null) { matchRuleDesc.append(writeQDescrs(attr)); count++; } attr = attrs.get(DESC_ID); if (attr != null) { matchRuleDesc.append(writeQDString(attr)); count++; } attr = attrs.get(OBSOLETE_ID); if (attr != null) { matchRuleDesc.append(writeBoolean(attr)); count++; } attr = attrs.get(SYNTAX_ID); if (attr != null) { matchRuleDesc.append(writeWOID(attr)); count++; } else { throw new ConfigurationException("Attribute type doesn't" + "have a syntax OID"); } // process any remaining attributes if (count < attrs.size()) { String attrId = null; // use enumeration because attribute ID is not known for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) { attr = (Attribute)ae.next(); attrId = attr.getID(); // skip those already processed if (attrId.equals(NUMERICOID_ID) || attrId.equals(NAME_ID) || attrId.equals(SYNTAX_ID) || attrId.equals(DESC_ID) || attrId.equals(OBSOLETE_ID)) { continue; } else { matchRuleDesc.append(writeQDStrings(attr)); } } } matchRuleDesc.append(")"); return matchRuleDesc.toString(); } final private String writeNumericOID(Attribute nOIDAttr) throws NamingException { if(nOIDAttr.size() != 1) { throw new InvalidAttributeValueException( "A class definition must have exactly one numeric OID"); } return (String)(nOIDAttr.get()) + WHSP; } final private String writeWOID(Attribute attr) throws NamingException { if (netscapeBug) return writeQDString(attr); else return attr.getID() + WHSP + attr.get() + WHSP; } /* qdescr = whsp "'" descr "'" whsp */ final private String writeQDString(Attribute qdStringAttr) throws NamingException { if(qdStringAttr.size() != 1) { throw new InvalidAttributeValueException( qdStringAttr.getID() + " must have exactly one value"); } return qdStringAttr.getID() + WHSP + SINGLE_QUOTE + qdStringAttr.get() + SINGLE_QUOTE + WHSP; } /** * dstring = 1*utf8 * qdstring = whsp "'" dstring "'" whsp * qdstringlist = [ qdstring *( qdstring ) ] * qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp ) */ private final String writeQDStrings(Attribute attr) throws NamingException { return writeQDescrs(attr); } /** * qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp ) * qdescrlist = [ qdescr *( qdescr ) ] * qdescr = whsp "'" descr "'" whsp * descr = keystring */ private final String writeQDescrs(Attribute attr) throws NamingException { switch(attr.size()) { case 0: throw new InvalidAttributeValueException( attr.getID() + "has no values"); case 1: return writeQDString(attr); } // write QDList StringBuffer qdList = new StringBuffer(attr.getID()); qdList.append(WHSP); qdList.append(OID_LIST_BEGIN); NamingEnumeration values = attr.getAll(); while(values.hasMore()) { qdList.append(WHSP); qdList.append(SINGLE_QUOTE); qdList.append((String)values.next()); qdList.append(SINGLE_QUOTE); qdList.append(WHSP); } qdList.append(OID_LIST_END); qdList.append(WHSP); return qdList.toString(); } final private String writeOIDs(Attribute oidsAttr) throws NamingException { switch(oidsAttr.size()) { case 0: throw new InvalidAttributeValueException( oidsAttr.getID() + "has no values"); case 1: if (netscapeBug) { break; // %%% write out as list to avoid crashing server } return writeWOID(oidsAttr); } // write OID List StringBuffer oidList = new StringBuffer(oidsAttr.getID()); oidList.append(WHSP); oidList.append(OID_LIST_BEGIN); NamingEnumeration values = oidsAttr.getAll(); oidList.append(WHSP); oidList.append(values.next()); while(values.hasMore()) { oidList.append(WHSP); oidList.append(OID_SEPARATOR); oidList.append(WHSP); oidList.append((String)values.next()); } oidList.append(WHSP); oidList.append(OID_LIST_END); oidList.append(WHSP); return oidList.toString(); } private final String writeBoolean(Attribute booleanAttr) throws NamingException { return booleanAttr.getID() + WHSP; } /** * Returns an attribute for updating the Object Class Definition schema * attribute */ final Attribute stringifyObjDesc(Attributes classDefAttrs) throws NamingException { Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID); objDescAttr.add(classDef2ObjectDesc(classDefAttrs)); return objDescAttr; } /** * Returns an attribute for updating the Attribute Definition schema attribute */ final Attribute stringifyAttrDesc(Attributes attrDefAttrs) throws NamingException { Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID); attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs)); return attrDescAttr; } /** * Returns an attribute for updating the Syntax schema attribute */ final Attribute stringifySyntaxDesc(Attributes syntaxDefAttrs) throws NamingException { Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID); syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs)); return syntaxDescAttr; } /** * Returns an attribute for updating the Matching Rule schema attribute */ final Attribute stringifyMatchRuleDesc(Attributes matchRuleDefAttrs) throws NamingException { Attribute matchRuleDescAttr = new BasicAttribute(MATCHRULEDESC_ATTR_ID); matchRuleDescAttr.add(matchRuleDef2MatchRuleDesc(matchRuleDefAttrs)); return matchRuleDescAttr; } }