0001: /*
0002: * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.jndi.ldap;
0027:
0028: import javax.naming.*;
0029: import javax.naming.directory.*;
0030: import java.util.Hashtable;
0031: import java.util.Vector;
0032:
0033: /**
0034: * Netscape's 3.1 servers have some schema bugs:
0035: * - It puts quotes around OIDs (such as those for SUP, SYNTAX).
0036: * - When you try to write out the MUST/MAY list (such as "MUST cn"),
0037: * it wants ("MUST (cn)") instead
0038: */
0039:
0040: final class LdapSchemaParser {
0041:
0042: // do debugging
0043: private static final boolean debug = false;
0044:
0045: // names of attribute IDs in the LDAP schema entry
0046: static final String OBJECTCLASSDESC_ATTR_ID = "objectClasses";
0047: static final String ATTRIBUTEDESC_ATTR_ID = "attributeTypes";
0048: static final String SYNTAXDESC_ATTR_ID = "ldapSyntaxes";
0049: static final String MATCHRULEDESC_ATTR_ID = "matchingRules";
0050:
0051: // information for creating internal nodes in JNDI schema tree
0052: static final String OBJECTCLASS_DEFINITION_NAME = "ClassDefinition";
0053: private static final String[] CLASS_DEF_ATTRS = { "objectclass",
0054: "ClassDefinition" };
0055: static final String ATTRIBUTE_DEFINITION_NAME = "AttributeDefinition";
0056: private static final String[] ATTR_DEF_ATTRS = { "objectclass",
0057: "AttributeDefinition" };
0058: static final String SYNTAX_DEFINITION_NAME = "SyntaxDefinition";
0059: private static final String[] SYNTAX_DEF_ATTRS = { "objectclass",
0060: "SyntaxDefinition" };
0061: static final String MATCHRULE_DEFINITION_NAME = "MatchingRule";
0062: private static final String[] MATCHRULE_DEF_ATTRS = {
0063: "objectclass", "MatchingRule" };
0064:
0065: // special tokens used in LDAP schema descriptions
0066: private static final char SINGLE_QUOTE = '\'';
0067: private static final char WHSP = ' ';
0068: private static final char OID_LIST_BEGIN = '(';
0069: private static final char OID_LIST_END = ')';
0070: private static final char OID_SEPARATOR = '$';
0071:
0072: // common IDs
0073: private static final String NUMERICOID_ID = "NUMERICOID";
0074: private static final String NAME_ID = "NAME";
0075: private static final String DESC_ID = "DESC";
0076: private static final String OBSOLETE_ID = "OBSOLETE";
0077: private static final String SUP_ID = "SUP";
0078: private static final String PRIVATE_ID = "X-";
0079:
0080: // Object Class specific IDs
0081: private static final String ABSTRACT_ID = "ABSTRACT";
0082: private static final String STRUCTURAL_ID = "STRUCTURAL";
0083: private static final String AUXILARY_ID = "AUXILIARY";
0084: private static final String MUST_ID = "MUST";
0085: private static final String MAY_ID = "MAY";
0086:
0087: // Attribute Type specific IDs
0088: private static final String EQUALITY_ID = "EQUALITY";
0089: private static final String ORDERING_ID = "ORDERING";
0090: private static final String SUBSTR_ID = "SUBSTR";
0091: private static final String SYNTAX_ID = "SYNTAX";
0092: private static final String SINGLE_VAL_ID = "SINGLE-VALUE";
0093: private static final String COLLECTIVE_ID = "COLLECTIVE";
0094: private static final String NO_USER_MOD_ID = "NO-USER-MODIFICATION";
0095: private static final String USAGE_ID = "USAGE";
0096:
0097: // The string value we give to boolean variables
0098: private static final String SCHEMA_TRUE_VALUE = "true";
0099:
0100: // To get around writing schemas that crash Netscape server
0101: private boolean netscapeBug;
0102:
0103: LdapSchemaParser(boolean netscapeBug) {
0104: this .netscapeBug = netscapeBug;
0105: }
0106:
0107: final static void LDAP2JNDISchema(Attributes schemaAttrs,
0108: LdapSchemaCtx schemaRoot) throws NamingException {
0109: Attribute objectClassesAttr = null;
0110: Attribute attributeDefAttr = null;
0111: Attribute syntaxDefAttr = null;
0112: Attribute matchRuleDefAttr = null;
0113:
0114: objectClassesAttr = schemaAttrs.get(OBJECTCLASSDESC_ATTR_ID);
0115: if (objectClassesAttr != null) {
0116: objectDescs2ClassDefs(objectClassesAttr, schemaRoot);
0117: }
0118:
0119: attributeDefAttr = schemaAttrs.get(ATTRIBUTEDESC_ATTR_ID);
0120: if (attributeDefAttr != null) {
0121: attrDescs2AttrDefs(attributeDefAttr, schemaRoot);
0122: }
0123:
0124: syntaxDefAttr = schemaAttrs.get(SYNTAXDESC_ATTR_ID);
0125: if (syntaxDefAttr != null) {
0126: syntaxDescs2SyntaxDefs(syntaxDefAttr, schemaRoot);
0127: }
0128:
0129: matchRuleDefAttr = schemaAttrs.get(MATCHRULEDESC_ATTR_ID);
0130: if (matchRuleDefAttr != null) {
0131: matchRuleDescs2MatchRuleDefs(matchRuleDefAttr, schemaRoot);
0132: }
0133: }
0134:
0135: final private static DirContext objectDescs2ClassDefs(
0136: Attribute objDescsAttr, LdapSchemaCtx schemaRoot)
0137: throws NamingException {
0138:
0139: NamingEnumeration objDescs;
0140: Attributes objDef;
0141: LdapSchemaCtx classDefTree;
0142:
0143: // create the class def subtree
0144: Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
0145: attrs.put(CLASS_DEF_ATTRS[0], CLASS_DEF_ATTRS[1]);
0146: classDefTree = schemaRoot.setup(LdapSchemaCtx.OBJECTCLASS_ROOT,
0147: OBJECTCLASS_DEFINITION_NAME, attrs);
0148:
0149: objDescs = objDescsAttr.getAll();
0150: String currentName;
0151: while (objDescs.hasMore()) {
0152: String objDesc = (String) objDescs.next();
0153: try {
0154: Object[] def = desc2Def(objDesc);
0155: currentName = (String) def[0];
0156: objDef = (Attributes) def[1];
0157: classDefTree.setup(LdapSchemaCtx.OBJECTCLASS,
0158: currentName, objDef);
0159: } catch (NamingException ne) {
0160: // error occurred while parsing, ignore current entry
0161: }
0162: }
0163:
0164: return classDefTree;
0165: }
0166:
0167: final private static DirContext attrDescs2AttrDefs(
0168: Attribute attributeDescAttr, LdapSchemaCtx schemaRoot)
0169: throws NamingException {
0170:
0171: NamingEnumeration attrDescs;
0172: Attributes attrDef;
0173: LdapSchemaCtx attrDefTree;
0174:
0175: // create the AttributeDef subtree
0176: Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
0177: attrs.put(ATTR_DEF_ATTRS[0], ATTR_DEF_ATTRS[1]);
0178: attrDefTree = schemaRoot.setup(LdapSchemaCtx.ATTRIBUTE_ROOT,
0179: ATTRIBUTE_DEFINITION_NAME, attrs);
0180:
0181: attrDescs = attributeDescAttr.getAll();
0182: String currentName;
0183: while (attrDescs.hasMore()) {
0184: String attrDesc = (String) attrDescs.next();
0185: try {
0186: Object[] def = desc2Def(attrDesc);
0187: currentName = (String) def[0];
0188: attrDef = (Attributes) def[1];
0189: attrDefTree.setup(LdapSchemaCtx.ATTRIBUTE, currentName,
0190: attrDef);
0191: } catch (NamingException ne) {
0192: // error occurred while parsing, ignore current entry
0193: }
0194: }
0195:
0196: return attrDefTree;
0197: }
0198:
0199: final private static DirContext syntaxDescs2SyntaxDefs(
0200: Attribute syntaxDescAttr, LdapSchemaCtx schemaRoot)
0201: throws NamingException {
0202:
0203: NamingEnumeration syntaxDescs;
0204: Attributes syntaxDef;
0205: LdapSchemaCtx syntaxDefTree;
0206:
0207: // create the SyntaxDef subtree
0208: Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
0209: attrs.put(SYNTAX_DEF_ATTRS[0], SYNTAX_DEF_ATTRS[1]);
0210: syntaxDefTree = schemaRoot.setup(LdapSchemaCtx.SYNTAX_ROOT,
0211: SYNTAX_DEFINITION_NAME, attrs);
0212:
0213: syntaxDescs = syntaxDescAttr.getAll();
0214: String currentName;
0215: while (syntaxDescs.hasMore()) {
0216: String syntaxDesc = (String) syntaxDescs.next();
0217: try {
0218: Object[] def = desc2Def(syntaxDesc);
0219: currentName = (String) def[0];
0220: syntaxDef = (Attributes) def[1];
0221: syntaxDefTree.setup(LdapSchemaCtx.SYNTAX, currentName,
0222: syntaxDef);
0223: } catch (NamingException ne) {
0224: // error occurred while parsing, ignore current entry
0225: }
0226: }
0227:
0228: return syntaxDefTree;
0229: }
0230:
0231: final private static DirContext matchRuleDescs2MatchRuleDefs(
0232: Attribute matchRuleDescAttr, LdapSchemaCtx schemaRoot)
0233: throws NamingException {
0234:
0235: NamingEnumeration matchRuleDescs;
0236: Attributes matchRuleDef;
0237: LdapSchemaCtx matchRuleDefTree;
0238:
0239: // create the MatchRuleDef subtree
0240: Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
0241: attrs.put(MATCHRULE_DEF_ATTRS[0], MATCHRULE_DEF_ATTRS[1]);
0242: matchRuleDefTree = schemaRoot.setup(
0243: LdapSchemaCtx.MATCHRULE_ROOT,
0244: MATCHRULE_DEFINITION_NAME, attrs);
0245:
0246: matchRuleDescs = matchRuleDescAttr.getAll();
0247: String currentName;
0248: while (matchRuleDescs.hasMore()) {
0249: String matchRuleDesc = (String) matchRuleDescs.next();
0250: try {
0251: Object[] def = desc2Def(matchRuleDesc);
0252: currentName = (String) def[0];
0253: matchRuleDef = (Attributes) def[1];
0254: matchRuleDefTree.setup(LdapSchemaCtx.MATCHRULE,
0255: currentName, matchRuleDef);
0256: } catch (NamingException ne) {
0257: // error occurred while parsing, ignore current entry
0258: }
0259: }
0260:
0261: return matchRuleDefTree;
0262: }
0263:
0264: final private static Object[] desc2Def(String desc)
0265: throws NamingException {
0266: //System.err.println(desc);
0267:
0268: Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
0269: Attribute attr = null;
0270: int[] pos = new int[] { 1 }; // tolerate missing leading space
0271: boolean moreTags = true;
0272:
0273: // Always begins with <whsp numericoid whsp>
0274: attr = readNumericOID(desc, pos);
0275: String currentName = (String) attr.get(0); // name is OID by default
0276: attrs.put(attr);
0277:
0278: skipWhitespace(desc, pos);
0279:
0280: while (moreTags) {
0281: attr = readNextTag(desc, pos);
0282: attrs.put(attr);
0283:
0284: if (attr.getID().equals(NAME_ID)) {
0285: currentName = (String) attr.get(0); // use NAME attribute as name
0286: }
0287:
0288: skipWhitespace(desc, pos);
0289:
0290: if (pos[0] >= desc.length() - 1) {
0291: moreTags = false;
0292: }
0293: }
0294:
0295: return new Object[] { currentName, attrs };
0296: }
0297:
0298: // returns the index of the first whitespace char of a linear whitspace
0299: // sequince ending at the given position.
0300: final private static int findTrailingWhitespace(String string,
0301: int pos) {
0302: for (int i = pos; i > 0; i--) {
0303: if (string.charAt(i) != WHSP) {
0304: return i + 1;
0305: }
0306: }
0307: return 0;
0308: }
0309:
0310: final private static void skipWhitespace(String string, int[] pos) {
0311: for (int i = pos[0]; i < string.length(); i++) {
0312: if (string.charAt(i) != WHSP) {
0313: pos[0] = i;
0314: if (debug) {
0315: System.err.println("skipWhitespace: skipping to "
0316: + i);
0317: }
0318: return;
0319: }
0320: }
0321: }
0322:
0323: final private static Attribute readNumericOID(String string,
0324: int[] pos) throws NamingException {
0325:
0326: if (debug) {
0327: System.err.println("readNumericoid: pos=" + pos[0]);
0328: }
0329:
0330: int begin, end;
0331: String value = null;
0332:
0333: skipWhitespace(string, pos);
0334:
0335: begin = pos[0];
0336: end = string.indexOf(WHSP, begin);
0337:
0338: if (end == -1 || end - begin < 1) {
0339: throw new InvalidAttributeValueException(
0340: "no numericoid found: " + string);
0341: }
0342:
0343: value = string.substring(begin, end);
0344:
0345: pos[0] += value.length();
0346:
0347: return new BasicAttribute(NUMERICOID_ID, value);
0348: }
0349:
0350: final private static Attribute readNextTag(String string, int[] pos)
0351: throws NamingException {
0352:
0353: Attribute attr = null;
0354: String tagName = null;
0355: String[] values = null;
0356:
0357: skipWhitespace(string, pos);
0358:
0359: if (debug) {
0360: System.err.println("readNextTag: pos=" + pos[0]);
0361: }
0362:
0363: // get the name and values of the attribute to return
0364: int trailingSpace = string.indexOf(WHSP, pos[0]);
0365:
0366: // tolerate a schema that omits the trailing space
0367: if (trailingSpace < 0) {
0368: tagName = string.substring(pos[0], string.length() - 1);
0369: } else {
0370: tagName = string.substring(pos[0], trailingSpace);
0371: }
0372:
0373: values = readTag(tagName, string, pos);
0374:
0375: // make sure at least one value was returned
0376: if (values.length < 0) {
0377: throw new InvalidAttributeValueException("no values for "
0378: + "attribute \"" + tagName + "\"");
0379: }
0380:
0381: // create the attribute, using the first value
0382: attr = new BasicAttribute(tagName, values[0]);
0383:
0384: // add other values if there are any
0385: for (int i = 1; i < values.length; i++) {
0386: attr.add(values[i]);
0387: }
0388:
0389: return attr;
0390: }
0391:
0392: final private static String[] readTag(String tag, String string,
0393: int[] pos) throws NamingException {
0394:
0395: if (debug) {
0396: System.err.println("ReadTag: " + tag + " pos=" + pos[0]);
0397: }
0398:
0399: // move parser past tag name
0400: pos[0] += tag.length();
0401: skipWhitespace(string, pos);
0402:
0403: if (tag.equals(NAME_ID)) {
0404: return readQDescrs(string, pos); // names[0] is NAME
0405: }
0406:
0407: if (tag.equals(DESC_ID)) {
0408: return readQDString(string, pos);
0409: }
0410:
0411: if (tag.equals(EQUALITY_ID) || tag.equals(ORDERING_ID)
0412: || tag.equals(SUBSTR_ID) || tag.equals(SYNTAX_ID)) {
0413: return readWOID(string, pos);
0414: }
0415:
0416: if (tag.equals(OBSOLETE_ID) || tag.equals(ABSTRACT_ID)
0417: || tag.equals(STRUCTURAL_ID) || tag.equals(AUXILARY_ID)
0418: || tag.equals(SINGLE_VAL_ID)
0419: || tag.equals(COLLECTIVE_ID)
0420: || tag.equals(NO_USER_MOD_ID)) {
0421: return new String[] { SCHEMA_TRUE_VALUE };
0422: }
0423:
0424: if (tag.equals(SUP_ID)
0425: || // oid list for object class; WOID for attribute
0426: tag.equals(MUST_ID) || tag.equals(MAY_ID)
0427: || tag.equals(USAGE_ID)) {
0428: return readOIDs(string, pos);
0429: }
0430:
0431: // otherwise it's a schema element with a quoted string value
0432: return readQDStrings(string, pos);
0433: }
0434:
0435: final private static String[] readQDString(String string, int[] pos)
0436: throws NamingException {
0437:
0438: int begin, end;
0439:
0440: begin = string.indexOf(SINGLE_QUOTE, pos[0]) + 1;
0441: end = string.indexOf(SINGLE_QUOTE, begin);
0442:
0443: if (debug) {
0444: System.err.println("ReadQDString: pos=" + pos[0]
0445: + " begin=" + begin + " end=" + end);
0446: }
0447:
0448: if (begin == -1 || end == -1 || begin == end) {
0449: throw new InvalidAttributeIdentifierException("malformed "
0450: + "QDString: " + string);
0451: }
0452:
0453: // make sure the qdstring end symbol is there
0454: if (string.charAt(begin - 1) != SINGLE_QUOTE) {
0455: throw new InvalidAttributeIdentifierException(
0456: "qdstring has " + "no end mark: " + string);
0457: }
0458:
0459: pos[0] = end + 1;
0460: return new String[] { string.substring(begin, end) };
0461: }
0462:
0463: /**
0464: * dstring = 1*utf8
0465: * qdstring = whsp "'" dstring "'" whsp
0466: * qdstringlist = [ qdstring *( qdstring ) ]
0467: * qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )
0468: */
0469: private final static String[] readQDStrings(String string, int[] pos)
0470: throws NamingException {
0471:
0472: return readQDescrs(string, pos);
0473: }
0474:
0475: /**
0476: * ; object descriptors used as schema element names
0477: * qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp )
0478: * qdescrlist = [ qdescr *( qdescr ) ]
0479: * qdescr = whsp "'" descr "'" whsp
0480: * descr = keystring
0481: */
0482: final private static String[] readQDescrs(String string, int[] pos)
0483: throws NamingException {
0484:
0485: if (debug) {
0486: System.err.println("readQDescrs: pos=" + pos[0]);
0487: }
0488:
0489: skipWhitespace(string, pos);
0490:
0491: switch (string.charAt(pos[0])) {
0492: case OID_LIST_BEGIN:
0493: return readQDescrList(string, pos);
0494: case SINGLE_QUOTE:
0495: return readQDString(string, pos);
0496: default:
0497: throw new InvalidAttributeValueException("unexpected oids "
0498: + "string: " + string);
0499: }
0500: }
0501:
0502: /**
0503: * qdescrlist = [ qdescr *( qdescr ) ]
0504: * qdescr = whsp "'" descr "'" whsp
0505: * descr = keystring
0506: */
0507: final private static String[] readQDescrList(String string,
0508: int[] pos) throws NamingException {
0509:
0510: int begin, end;
0511: Vector values = new Vector(5);
0512:
0513: if (debug) {
0514: System.err.println("ReadQDescrList: pos=" + pos[0]);
0515: }
0516:
0517: pos[0]++; // skip '('
0518: skipWhitespace(string, pos);
0519: begin = pos[0];
0520: end = string.indexOf(OID_LIST_END, begin);
0521:
0522: if (end == -1) {
0523: throw new InvalidAttributeValueException(
0524: "oidlist has no end " + "mark: " + string);
0525: }
0526:
0527: while (begin < end) {
0528: String[] one = readQDString(string, pos);
0529:
0530: if (debug) {
0531: System.err.println("ReadQDescrList: found '" + one[0]
0532: + "' at begin=" + begin + " end =" + end);
0533: }
0534:
0535: values.addElement(one[0]);
0536: skipWhitespace(string, pos);
0537: begin = pos[0];
0538: }
0539:
0540: pos[0] = end + 1; // skip ')'
0541:
0542: String[] answer = new String[values.size()];
0543: for (int i = 0; i < answer.length; i++) {
0544: answer[i] = (String) values.elementAt(i);
0545: }
0546: return answer;
0547: }
0548:
0549: final private static String[] readWOID(String string, int[] pos)
0550: throws NamingException {
0551:
0552: if (debug) {
0553: System.err.println("readWOIDs: pos=" + pos[0]);
0554: }
0555:
0556: skipWhitespace(string, pos);
0557:
0558: if (string.charAt(pos[0]) == SINGLE_QUOTE) {
0559: // %%% workaround for Netscape schema bug
0560: return readQDString(string, pos);
0561: }
0562:
0563: int begin, end;
0564:
0565: begin = pos[0];
0566: end = string.indexOf(WHSP, begin);
0567:
0568: if (debug) {
0569: System.err.println("ReadWOID: pos=" + pos[0] + " begin="
0570: + begin + " end=" + end);
0571: }
0572:
0573: if (end == -1 || begin == end) {
0574: throw new InvalidAttributeIdentifierException("malformed "
0575: + "OID: " + string);
0576: }
0577: pos[0] = end + 1;
0578:
0579: return new String[] { string.substring(begin, end) };
0580: }
0581:
0582: /*
0583: * oids = woid / ( "(" oidlist ")" )
0584: * oidlist = woid *( "$" woid )
0585: */
0586: final private static String[] readOIDs(String string, int[] pos)
0587: throws NamingException {
0588:
0589: if (debug) {
0590: System.err.println("readOIDs: pos=" + pos[0]);
0591: }
0592:
0593: skipWhitespace(string, pos);
0594:
0595: // Single OID
0596: if (string.charAt(pos[0]) != OID_LIST_BEGIN) {
0597: return readWOID(string, pos);
0598: }
0599:
0600: // Multiple OIDs
0601:
0602: int begin, cur, end;
0603: String oidName = null;
0604: Vector values = new Vector(5);
0605:
0606: if (debug) {
0607: System.err.println("ReadOIDList: pos=" + pos[0]);
0608: }
0609:
0610: pos[0]++;
0611: skipWhitespace(string, pos);
0612: begin = pos[0];
0613: end = string.indexOf(OID_LIST_END, begin);
0614: cur = string.indexOf(OID_SEPARATOR, begin);
0615:
0616: if (end == -1) {
0617: throw new InvalidAttributeValueException(
0618: "oidlist has no end " + "mark: " + string);
0619: }
0620:
0621: if (cur == -1 || end < cur) {
0622: cur = end;
0623: }
0624:
0625: while (cur < end && cur > 0) {
0626: int wsBegin = findTrailingWhitespace(string, cur - 1);
0627: oidName = string.substring(begin, wsBegin);
0628: if (debug) {
0629: System.err.println("ReadOIDList: found '" + oidName
0630: + "' at begin=" + begin + " end =" + end);
0631: }
0632: values.addElement(oidName);
0633: pos[0] = cur + 1;
0634: skipWhitespace(string, pos);
0635: begin = pos[0];
0636: cur = string.indexOf(OID_SEPARATOR, begin);
0637: if (debug) {
0638: System.err.println("ReadOIDList: begin = " + begin);
0639: }
0640: }
0641:
0642: if (debug) {
0643: System.err.println("ReadOIDList: found '" + oidName
0644: + "' at begin=" + begin + " end =" + end);
0645: }
0646:
0647: int wsBegin = findTrailingWhitespace(string, end - 1);
0648: oidName = string.substring(begin, wsBegin);
0649: values.addElement(oidName);
0650:
0651: pos[0] = end + 1;
0652:
0653: String[] answer = new String[values.size()];
0654: for (int i = 0; i < answer.length; i++) {
0655: answer[i] = (String) values.elementAt(i);
0656: }
0657: return answer;
0658: }
0659:
0660: // ----------------- "unparser" methods
0661: // Methods that are used for translating a node in the schema tree
0662: // into RFC2252 format for storage back into the LDAP directory
0663: /*
0664: static Attributes JNDI2LDAPSchema(DirContext schemaRoot)
0665: throws NamingException {
0666:
0667: Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID);
0668: Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID);
0669: Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID);
0670: Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
0671: DirContext classDefs, attributeDefs, syntaxDefs;
0672: Attributes classDefsAttrs, attributeDefsAttrs, syntaxDefsAttrs;
0673: NamingEnumeration defs;
0674: Object obj;
0675: int i = 0;
0676:
0677: try {
0678: obj = schemaRoot.lookup(OBJECTCLASS_DEFINITION_NAME);
0679: if(obj != null && obj instanceof DirContext) {
0680: classDefs = (DirContext)obj;
0681: defs = classDefs.listBindings("");
0682: while(defs.hasMoreElements()) {
0683: i++;
0684: DirContext classDef = (DirContext)
0685: ((Binding)(defs.next())).getObject();
0686: classDefAttrs = classDef.getAttributes("");
0687: objDescAttr.add(classDef2ObjectDesc(classDefAttrs));
0688: }
0689: if (debug)
0690: System.err.println(i + " total object classes");
0691: attrs.put(objDescAttr);
0692: } else {
0693: throw new NamingException(
0694: "Problem with Schema tree: the object named " +
0695: OBJECTCLASS_DEFINITION_NAME + " is not a " +
0696: "DirContext");
0697: }
0698: } catch (NameNotFoundException e) {} // ignore
0699:
0700: i=0;
0701: try {
0702: obj = schemaRoot.lookup(ATTRIBUTE_DEFINITION_NAME);
0703: if(obj instanceof DirContext) {
0704: attributeDefs = (DirContext)obj;
0705: defs = attributeDefs.listBindings("");
0706: while(defs.hasMoreElements()) {
0707: i++;
0708: DirContext attrDef = (DirContext)
0709: ((Binding)defs.next()).getObject();
0710: attrDefAttrs = attrDef.getAttributes("");
0711: attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs));
0712: }
0713: if (debug)
0714: System.err.println(i + " attribute definitions");
0715: attrs.put(attrDescAttr);
0716: } else {
0717: throw new NamingException(
0718: "Problem with schema tree: the object named " +
0719: ATTRIBUTE_DEFINITION_NAME + " is not a " +
0720: "DirContext");
0721: }
0722: } catch (NameNotFoundException e) {} // ignore
0723:
0724: i=0;
0725: try {
0726: obj = schemaRoot.lookup(SYNTAX_DEFINITION_NAME);
0727: if(obj instanceof DirContext) {
0728: syntaxDefs = (DirContext)obj;
0729: defs =syntaxDefs.listBindings("");
0730: while(defs.hasMoreElements()) {
0731: i++;
0732: DirContext syntaxDef = (DirContext)
0733: ((Binding)defs.next()).getObject();
0734: syntaxDefAttrs = syntaxDef.getAttributes("");
0735: syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs));
0736: }
0737: if (debug)
0738: System.err.println(i + " total syntax definitions");
0739: attrs.put(syntaxDescAttr);
0740: } else {
0741: throw new NamingException(
0742: "Problem with schema tree: the object named " +
0743: SYNTAX_DEFINITION_NAME + " is not a " +
0744: "DirContext");
0745: }
0746: } catch (NameNotFoundException e) {} // ignore
0747:
0748: return attrs;
0749: }
0750:
0751: */
0752:
0753: /**
0754: * Translate attributes that describe an object class into the
0755: * string description as defined in RFC 2252.
0756: */
0757: final private String classDef2ObjectDesc(Attributes attrs)
0758: throws NamingException {
0759:
0760: StringBuffer objectDesc = new StringBuffer("( ");
0761:
0762: Attribute attr = null;
0763: int count = 0;
0764:
0765: // extract attributes by ID to guarantee ordering
0766:
0767: attr = attrs.get(NUMERICOID_ID);
0768: if (attr != null) {
0769: objectDesc.append(writeNumericOID(attr));
0770: count++;
0771: } else {
0772: throw new ConfigurationException("Class definition doesn't"
0773: + "have a numeric OID");
0774: }
0775:
0776: attr = attrs.get(NAME_ID);
0777: if (attr != null) {
0778: objectDesc.append(writeQDescrs(attr));
0779: count++;
0780: }
0781:
0782: attr = attrs.get(DESC_ID);
0783: if (attr != null) {
0784: objectDesc.append(writeQDString(attr));
0785: count++;
0786: }
0787:
0788: attr = attrs.get(OBSOLETE_ID);
0789: if (attr != null) {
0790: objectDesc.append(writeBoolean(attr));
0791: count++;
0792: }
0793:
0794: attr = attrs.get(SUP_ID);
0795: if (attr != null) {
0796: objectDesc.append(writeOIDs(attr));
0797: count++;
0798: }
0799:
0800: attr = attrs.get(ABSTRACT_ID);
0801: if (attr != null) {
0802: objectDesc.append(writeBoolean(attr));
0803: count++;
0804: }
0805:
0806: attr = attrs.get(STRUCTURAL_ID);
0807: if (attr != null) {
0808: objectDesc.append(writeBoolean(attr));
0809: count++;
0810: }
0811:
0812: attr = attrs.get(AUXILARY_ID);
0813: if (attr != null) {
0814: objectDesc.append(writeBoolean(attr));
0815: count++;
0816: }
0817:
0818: attr = attrs.get(MUST_ID);
0819: if (attr != null) {
0820: objectDesc.append(writeOIDs(attr));
0821: count++;
0822: }
0823:
0824: attr = attrs.get(MAY_ID);
0825: if (attr != null) {
0826: objectDesc.append(writeOIDs(attr));
0827: count++;
0828: }
0829:
0830: // process any remaining attributes
0831: if (count < attrs.size()) {
0832: String attrId = null;
0833:
0834: // use enumeration because attribute ID is not known
0835: for (NamingEnumeration ae = attrs.getAll(); ae
0836: .hasMoreElements();) {
0837:
0838: attr = (Attribute) ae.next();
0839: attrId = attr.getID();
0840:
0841: // skip those already processed
0842: if (attrId.equals(NUMERICOID_ID)
0843: || attrId.equals(NAME_ID)
0844: || attrId.equals(SUP_ID)
0845: || attrId.equals(MAY_ID)
0846: || attrId.equals(MUST_ID)
0847: || attrId.equals(STRUCTURAL_ID)
0848: || attrId.equals(DESC_ID)
0849: || attrId.equals(AUXILARY_ID)
0850: || attrId.equals(ABSTRACT_ID)
0851: || attrId.equals(OBSOLETE_ID)) {
0852: continue;
0853:
0854: } else {
0855: objectDesc.append(writeQDStrings(attr));
0856: }
0857: }
0858: }
0859:
0860: objectDesc.append(")");
0861:
0862: return objectDesc.toString();
0863: }
0864:
0865: /**
0866: * Translate attributes that describe an attribute definition into the
0867: * string description as defined in RFC 2252.
0868: */
0869: final private String attrDef2AttrDesc(Attributes attrs)
0870: throws NamingException {
0871:
0872: StringBuffer attrDesc = new StringBuffer("( "); // opening parens
0873:
0874: Attribute attr = null;
0875: int count = 0;
0876:
0877: // extract attributes by ID to guarantee ordering
0878:
0879: attr = attrs.get(NUMERICOID_ID);
0880: if (attr != null) {
0881: attrDesc.append(writeNumericOID(attr));
0882: count++;
0883: } else {
0884: throw new ConfigurationException("Attribute type doesn't"
0885: + "have a numeric OID");
0886: }
0887:
0888: attr = attrs.get(NAME_ID);
0889: if (attr != null) {
0890: attrDesc.append(writeQDescrs(attr));
0891: count++;
0892: }
0893:
0894: attr = attrs.get(DESC_ID);
0895: if (attr != null) {
0896: attrDesc.append(writeQDString(attr));
0897: count++;
0898: }
0899:
0900: attr = attrs.get(OBSOLETE_ID);
0901: if (attr != null) {
0902: attrDesc.append(writeBoolean(attr));
0903: count++;
0904: }
0905:
0906: attr = attrs.get(SUP_ID);
0907: if (attr != null) {
0908: attrDesc.append(writeWOID(attr));
0909: count++;
0910: }
0911:
0912: attr = attrs.get(EQUALITY_ID);
0913: if (attr != null) {
0914: attrDesc.append(writeWOID(attr));
0915: count++;
0916: }
0917:
0918: attr = attrs.get(ORDERING_ID);
0919: if (attr != null) {
0920: attrDesc.append(writeWOID(attr));
0921: count++;
0922: }
0923:
0924: attr = attrs.get(SUBSTR_ID);
0925: if (attr != null) {
0926: attrDesc.append(writeWOID(attr));
0927: count++;
0928: }
0929:
0930: attr = attrs.get(SYNTAX_ID);
0931: if (attr != null) {
0932: attrDesc.append(writeWOID(attr));
0933: count++;
0934: }
0935:
0936: attr = attrs.get(SINGLE_VAL_ID);
0937: if (attr != null) {
0938: attrDesc.append(writeBoolean(attr));
0939: count++;
0940: }
0941:
0942: attr = attrs.get(COLLECTIVE_ID);
0943: if (attr != null) {
0944: attrDesc.append(writeBoolean(attr));
0945: count++;
0946: }
0947:
0948: attr = attrs.get(NO_USER_MOD_ID);
0949: if (attr != null) {
0950: attrDesc.append(writeBoolean(attr));
0951: count++;
0952: }
0953:
0954: attr = attrs.get(USAGE_ID);
0955: if (attr != null) {
0956: attrDesc.append(writeQDString(attr));
0957: count++;
0958: }
0959:
0960: // process any remaining attributes
0961: if (count < attrs.size()) {
0962: String attrId = null;
0963:
0964: // use enumeration because attribute ID is not known
0965: for (NamingEnumeration ae = attrs.getAll(); ae
0966: .hasMoreElements();) {
0967:
0968: attr = (Attribute) ae.next();
0969: attrId = attr.getID();
0970:
0971: // skip those already processed
0972: if (attrId.equals(NUMERICOID_ID)
0973: || attrId.equals(NAME_ID)
0974: || attrId.equals(SYNTAX_ID)
0975: || attrId.equals(DESC_ID)
0976: || attrId.equals(SINGLE_VAL_ID)
0977: || attrId.equals(EQUALITY_ID)
0978: || attrId.equals(ORDERING_ID)
0979: || attrId.equals(SUBSTR_ID)
0980: || attrId.equals(NO_USER_MOD_ID)
0981: || attrId.equals(USAGE_ID)
0982: || attrId.equals(SUP_ID)
0983: || attrId.equals(COLLECTIVE_ID)
0984: || attrId.equals(OBSOLETE_ID)) {
0985: continue;
0986:
0987: } else {
0988: attrDesc.append(writeQDStrings(attr));
0989: }
0990: }
0991: }
0992:
0993: attrDesc.append(")"); // add closing parens
0994:
0995: return attrDesc.toString();
0996: }
0997:
0998: /**
0999: * Translate attributes that describe an attribute syntax definition into the
1000: * string description as defined in RFC 2252.
1001: */
1002: final private String syntaxDef2SyntaxDesc(Attributes attrs)
1003: throws NamingException {
1004:
1005: StringBuffer syntaxDesc = new StringBuffer("( "); // opening parens
1006:
1007: Attribute attr = null;
1008: int count = 0;
1009:
1010: // extract attributes by ID to guarantee ordering
1011:
1012: attr = attrs.get(NUMERICOID_ID);
1013: if (attr != null) {
1014: syntaxDesc.append(writeNumericOID(attr));
1015: count++;
1016: } else {
1017: throw new ConfigurationException("Attribute type doesn't"
1018: + "have a numeric OID");
1019: }
1020:
1021: attr = attrs.get(DESC_ID);
1022: if (attr != null) {
1023: syntaxDesc.append(writeQDString(attr));
1024: count++;
1025: }
1026:
1027: // process any remaining attributes
1028: if (count < attrs.size()) {
1029: String attrId = null;
1030:
1031: // use enumeration because attribute ID is not known
1032: for (NamingEnumeration ae = attrs.getAll(); ae
1033: .hasMoreElements();) {
1034:
1035: attr = (Attribute) ae.next();
1036: attrId = attr.getID();
1037:
1038: // skip those already processed
1039: if (attrId.equals(NUMERICOID_ID)
1040: || attrId.equals(DESC_ID)) {
1041: continue;
1042:
1043: } else {
1044: syntaxDesc.append(writeQDStrings(attr));
1045: }
1046: }
1047: }
1048:
1049: syntaxDesc.append(")");
1050:
1051: return syntaxDesc.toString();
1052: }
1053:
1054: /**
1055: * Translate attributes that describe an attribute matching rule
1056: * definition into the string description as defined in RFC 2252.
1057: */
1058: final private String matchRuleDef2MatchRuleDesc(Attributes attrs)
1059: throws NamingException {
1060:
1061: StringBuffer matchRuleDesc = new StringBuffer("( "); // opening parens
1062:
1063: Attribute attr = null;
1064: int count = 0;
1065:
1066: // extract attributes by ID to guarantee ordering
1067:
1068: attr = attrs.get(NUMERICOID_ID);
1069: if (attr != null) {
1070: matchRuleDesc.append(writeNumericOID(attr));
1071: count++;
1072: } else {
1073: throw new ConfigurationException("Attribute type doesn't"
1074: + "have a numeric OID");
1075: }
1076:
1077: attr = attrs.get(NAME_ID);
1078: if (attr != null) {
1079: matchRuleDesc.append(writeQDescrs(attr));
1080: count++;
1081: }
1082:
1083: attr = attrs.get(DESC_ID);
1084: if (attr != null) {
1085: matchRuleDesc.append(writeQDString(attr));
1086: count++;
1087: }
1088:
1089: attr = attrs.get(OBSOLETE_ID);
1090: if (attr != null) {
1091: matchRuleDesc.append(writeBoolean(attr));
1092: count++;
1093: }
1094:
1095: attr = attrs.get(SYNTAX_ID);
1096: if (attr != null) {
1097: matchRuleDesc.append(writeWOID(attr));
1098: count++;
1099: } else {
1100: throw new ConfigurationException("Attribute type doesn't"
1101: + "have a syntax OID");
1102: }
1103:
1104: // process any remaining attributes
1105: if (count < attrs.size()) {
1106: String attrId = null;
1107:
1108: // use enumeration because attribute ID is not known
1109: for (NamingEnumeration ae = attrs.getAll(); ae
1110: .hasMoreElements();) {
1111:
1112: attr = (Attribute) ae.next();
1113: attrId = attr.getID();
1114:
1115: // skip those already processed
1116: if (attrId.equals(NUMERICOID_ID)
1117: || attrId.equals(NAME_ID)
1118: || attrId.equals(SYNTAX_ID)
1119: || attrId.equals(DESC_ID)
1120: || attrId.equals(OBSOLETE_ID)) {
1121: continue;
1122:
1123: } else {
1124: matchRuleDesc.append(writeQDStrings(attr));
1125: }
1126: }
1127: }
1128:
1129: matchRuleDesc.append(")");
1130:
1131: return matchRuleDesc.toString();
1132: }
1133:
1134: final private String writeNumericOID(Attribute nOIDAttr)
1135: throws NamingException {
1136: if (nOIDAttr.size() != 1) {
1137: throw new InvalidAttributeValueException(
1138: "A class definition must have exactly one numeric OID");
1139: }
1140: return (String) (nOIDAttr.get()) + WHSP;
1141: }
1142:
1143: final private String writeWOID(Attribute attr)
1144: throws NamingException {
1145: if (netscapeBug)
1146: return writeQDString(attr);
1147: else
1148: return attr.getID() + WHSP + attr.get() + WHSP;
1149: }
1150:
1151: /* qdescr = whsp "'" descr "'" whsp */
1152: final private String writeQDString(Attribute qdStringAttr)
1153: throws NamingException {
1154: if (qdStringAttr.size() != 1) {
1155: throw new InvalidAttributeValueException(qdStringAttr
1156: .getID()
1157: + " must have exactly one value");
1158: }
1159:
1160: return qdStringAttr.getID() + WHSP + SINGLE_QUOTE
1161: + qdStringAttr.get() + SINGLE_QUOTE + WHSP;
1162: }
1163:
1164: /**
1165: * dstring = 1*utf8
1166: * qdstring = whsp "'" dstring "'" whsp
1167: * qdstringlist = [ qdstring *( qdstring ) ]
1168: * qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )
1169: */
1170: private final String writeQDStrings(Attribute attr)
1171: throws NamingException {
1172: return writeQDescrs(attr);
1173: }
1174:
1175: /**
1176: * qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp )
1177: * qdescrlist = [ qdescr *( qdescr ) ]
1178: * qdescr = whsp "'" descr "'" whsp
1179: * descr = keystring
1180: */
1181: private final String writeQDescrs(Attribute attr)
1182: throws NamingException {
1183: switch (attr.size()) {
1184: case 0:
1185: throw new InvalidAttributeValueException(attr.getID()
1186: + "has no values");
1187: case 1:
1188: return writeQDString(attr);
1189: }
1190:
1191: // write QDList
1192:
1193: StringBuffer qdList = new StringBuffer(attr.getID());
1194: qdList.append(WHSP);
1195: qdList.append(OID_LIST_BEGIN);
1196:
1197: NamingEnumeration values = attr.getAll();
1198:
1199: while (values.hasMore()) {
1200: qdList.append(WHSP);
1201: qdList.append(SINGLE_QUOTE);
1202: qdList.append((String) values.next());
1203: qdList.append(SINGLE_QUOTE);
1204: qdList.append(WHSP);
1205: }
1206:
1207: qdList.append(OID_LIST_END);
1208: qdList.append(WHSP);
1209:
1210: return qdList.toString();
1211: }
1212:
1213: final private String writeOIDs(Attribute oidsAttr)
1214: throws NamingException {
1215:
1216: switch (oidsAttr.size()) {
1217: case 0:
1218: throw new InvalidAttributeValueException(oidsAttr.getID()
1219: + "has no values");
1220:
1221: case 1:
1222: if (netscapeBug) {
1223: break; // %%% write out as list to avoid crashing server
1224: }
1225: return writeWOID(oidsAttr);
1226: }
1227:
1228: // write OID List
1229:
1230: StringBuffer oidList = new StringBuffer(oidsAttr.getID());
1231: oidList.append(WHSP);
1232: oidList.append(OID_LIST_BEGIN);
1233:
1234: NamingEnumeration values = oidsAttr.getAll();
1235: oidList.append(WHSP);
1236: oidList.append(values.next());
1237:
1238: while (values.hasMore()) {
1239: oidList.append(WHSP);
1240: oidList.append(OID_SEPARATOR);
1241: oidList.append(WHSP);
1242: oidList.append((String) values.next());
1243: }
1244:
1245: oidList.append(WHSP);
1246: oidList.append(OID_LIST_END);
1247: oidList.append(WHSP);
1248:
1249: return oidList.toString();
1250: }
1251:
1252: private final String writeBoolean(Attribute booleanAttr)
1253: throws NamingException {
1254: return booleanAttr.getID() + WHSP;
1255: }
1256:
1257: /**
1258: * Returns an attribute for updating the Object Class Definition schema
1259: * attribute
1260: */
1261: final Attribute stringifyObjDesc(Attributes classDefAttrs)
1262: throws NamingException {
1263: Attribute objDescAttr = new BasicAttribute(
1264: OBJECTCLASSDESC_ATTR_ID);
1265: objDescAttr.add(classDef2ObjectDesc(classDefAttrs));
1266: return objDescAttr;
1267: }
1268:
1269: /**
1270: * Returns an attribute for updating the Attribute Definition schema attribute
1271: */
1272: final Attribute stringifyAttrDesc(Attributes attrDefAttrs)
1273: throws NamingException {
1274: Attribute attrDescAttr = new BasicAttribute(
1275: ATTRIBUTEDESC_ATTR_ID);
1276: attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs));
1277: return attrDescAttr;
1278: }
1279:
1280: /**
1281: * Returns an attribute for updating the Syntax schema attribute
1282: */
1283: final Attribute stringifySyntaxDesc(Attributes syntaxDefAttrs)
1284: throws NamingException {
1285: Attribute syntaxDescAttr = new BasicAttribute(
1286: SYNTAXDESC_ATTR_ID);
1287: syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs));
1288: return syntaxDescAttr;
1289: }
1290:
1291: /**
1292: * Returns an attribute for updating the Matching Rule schema attribute
1293: */
1294: final Attribute stringifyMatchRuleDesc(Attributes matchRuleDefAttrs)
1295: throws NamingException {
1296: Attribute matchRuleDescAttr = new BasicAttribute(
1297: MATCHRULEDESC_ATTR_ID);
1298: matchRuleDescAttr
1299: .add(matchRuleDef2MatchRuleDesc(matchRuleDefAttrs));
1300: return matchRuleDescAttr;
1301: }
1302: }
|