blob: 8d9dc475fc70e1a5e03767fd4e2f340af3099b94 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.jndi.ldap;
27
28import javax.naming.*;
29import javax.naming.directory.*;
30import java.util.Hashtable;
31import java.util.Vector;
32
33/**
34 * Netscape's 3.1 servers have some schema bugs:
35 * - It puts quotes around OIDs (such as those for SUP, SYNTAX).
36 * - When you try to write out the MUST/MAY list (such as "MUST cn"),
37 * it wants ("MUST (cn)") instead
38 */
39
40final class LdapSchemaParser {
41
42 // do debugging
43 private static final boolean debug = false;
44
45
46 // names of attribute IDs in the LDAP schema entry
47 static final String OBJECTCLASSDESC_ATTR_ID = "objectClasses";
48 static final String ATTRIBUTEDESC_ATTR_ID = "attributeTypes";
49 static final String SYNTAXDESC_ATTR_ID = "ldapSyntaxes";
50 static final String MATCHRULEDESC_ATTR_ID = "matchingRules";
51
52 // information for creating internal nodes in JNDI schema tree
53 static final String OBJECTCLASS_DEFINITION_NAME =
54 "ClassDefinition";
55 private static final String[] CLASS_DEF_ATTRS = {
56 "objectclass", "ClassDefinition"};
57 static final String ATTRIBUTE_DEFINITION_NAME =
58 "AttributeDefinition";
59 private static final String[] ATTR_DEF_ATTRS = {
60 "objectclass", "AttributeDefinition" };
61 static final String SYNTAX_DEFINITION_NAME =
62 "SyntaxDefinition";
63 private static final String[] SYNTAX_DEF_ATTRS = {
64 "objectclass", "SyntaxDefinition" };
65 static final String MATCHRULE_DEFINITION_NAME =
66 "MatchingRule";
67 private static final String[] MATCHRULE_DEF_ATTRS = {
68 "objectclass", "MatchingRule" };
69
70 // special tokens used in LDAP schema descriptions
71 private static final char SINGLE_QUOTE = '\'';
72 private static final char WHSP = ' ';
73 private static final char OID_LIST_BEGIN = '(';
74 private static final char OID_LIST_END = ')';
75 private static final char OID_SEPARATOR = '$';
76
77 // common IDs
78 private static final String NUMERICOID_ID = "NUMERICOID";
79 private static final String NAME_ID = "NAME";
80 private static final String DESC_ID = "DESC";
81 private static final String OBSOLETE_ID = "OBSOLETE";
82 private static final String SUP_ID = "SUP";
83 private static final String PRIVATE_ID = "X-";
84
85 // Object Class specific IDs
86 private static final String ABSTRACT_ID = "ABSTRACT";
87 private static final String STRUCTURAL_ID = "STRUCTURAL";
88 private static final String AUXILARY_ID = "AUXILIARY";
89 private static final String MUST_ID = "MUST";
90 private static final String MAY_ID = "MAY";
91
92 // Attribute Type specific IDs
93 private static final String EQUALITY_ID = "EQUALITY";
94 private static final String ORDERING_ID = "ORDERING";
95 private static final String SUBSTR_ID = "SUBSTR";
96 private static final String SYNTAX_ID = "SYNTAX";
97 private static final String SINGLE_VAL_ID = "SINGLE-VALUE";
98 private static final String COLLECTIVE_ID = "COLLECTIVE";
99 private static final String NO_USER_MOD_ID = "NO-USER-MODIFICATION";
100 private static final String USAGE_ID = "USAGE";
101
102 // The string value we give to boolean variables
103 private static final String SCHEMA_TRUE_VALUE = "true";
104
105 // To get around writing schemas that crash Netscape server
106 private boolean netscapeBug;
107
108 LdapSchemaParser(boolean netscapeBug) {
109 this.netscapeBug = netscapeBug;
110 }
111
112 final static void LDAP2JNDISchema(Attributes schemaAttrs,
113 LdapSchemaCtx schemaRoot) throws NamingException {
114 Attribute objectClassesAttr = null;
115 Attribute attributeDefAttr = null;
116 Attribute syntaxDefAttr = null;
117 Attribute matchRuleDefAttr = null;
118
119 objectClassesAttr = schemaAttrs.get(OBJECTCLASSDESC_ATTR_ID);
120 if(objectClassesAttr != null) {
121 objectDescs2ClassDefs(objectClassesAttr,schemaRoot);
122 }
123
124 attributeDefAttr = schemaAttrs.get(ATTRIBUTEDESC_ATTR_ID);
125 if(attributeDefAttr != null) {
126 attrDescs2AttrDefs(attributeDefAttr, schemaRoot);
127 }
128
129 syntaxDefAttr = schemaAttrs.get(SYNTAXDESC_ATTR_ID);
130 if(syntaxDefAttr != null) {
131 syntaxDescs2SyntaxDefs(syntaxDefAttr, schemaRoot);
132 }
133
134 matchRuleDefAttr = schemaAttrs.get(MATCHRULEDESC_ATTR_ID);
135 if(matchRuleDefAttr != null) {
136 matchRuleDescs2MatchRuleDefs(matchRuleDefAttr, schemaRoot);
137 }
138 }
139
140 final private static DirContext objectDescs2ClassDefs(Attribute objDescsAttr,
141 LdapSchemaCtx schemaRoot)
142 throws NamingException {
143
144 NamingEnumeration objDescs;
145 Attributes objDef;
146 LdapSchemaCtx classDefTree;
147
148 // create the class def subtree
149 Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
150 attrs.put(CLASS_DEF_ATTRS[0], CLASS_DEF_ATTRS[1]);
151 classDefTree = schemaRoot.setup(LdapSchemaCtx.OBJECTCLASS_ROOT,
152 OBJECTCLASS_DEFINITION_NAME, attrs);
153
154 objDescs = objDescsAttr.getAll();
155 String currentName;
156 while(objDescs.hasMore()) {
157 String objDesc = (String)objDescs.next();
158 try {
159 Object[] def = desc2Def(objDesc);
160 currentName = (String) def[0];
161 objDef = (Attributes) def[1];
162 classDefTree.setup(LdapSchemaCtx.OBJECTCLASS,
163 currentName, objDef);
164 } catch (NamingException ne) {
165 // error occurred while parsing, ignore current entry
166 }
167 }
168
169 return classDefTree;
170 }
171
172 final private static DirContext attrDescs2AttrDefs(Attribute attributeDescAttr,
173 LdapSchemaCtx schemaRoot)
174 throws NamingException {
175
176 NamingEnumeration attrDescs;
177 Attributes attrDef;
178 LdapSchemaCtx attrDefTree;
179
180 // create the AttributeDef subtree
181 Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
182 attrs.put(ATTR_DEF_ATTRS[0], ATTR_DEF_ATTRS[1]);
183 attrDefTree = schemaRoot.setup(LdapSchemaCtx.ATTRIBUTE_ROOT,
184 ATTRIBUTE_DEFINITION_NAME, attrs);
185
186 attrDescs = attributeDescAttr.getAll();
187 String currentName;
188 while(attrDescs.hasMore()) {
189 String attrDesc = (String)attrDescs.next();
190 try {
191 Object[] def = desc2Def(attrDesc);
192 currentName = (String) def[0];
193 attrDef = (Attributes) def[1];
194 attrDefTree.setup(LdapSchemaCtx.ATTRIBUTE,
195 currentName, attrDef);
196 } catch (NamingException ne) {
197 // error occurred while parsing, ignore current entry
198 }
199 }
200
201 return attrDefTree;
202 }
203
204 final private static DirContext syntaxDescs2SyntaxDefs(
205 Attribute syntaxDescAttr,
206 LdapSchemaCtx schemaRoot)
207 throws NamingException {
208
209 NamingEnumeration syntaxDescs;
210 Attributes syntaxDef;
211 LdapSchemaCtx syntaxDefTree;
212
213 // create the SyntaxDef subtree
214 Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
215 attrs.put(SYNTAX_DEF_ATTRS[0], SYNTAX_DEF_ATTRS[1]);
216 syntaxDefTree = schemaRoot.setup(LdapSchemaCtx.SYNTAX_ROOT,
217 SYNTAX_DEFINITION_NAME, attrs);
218
219 syntaxDescs = syntaxDescAttr.getAll();
220 String currentName;
221 while(syntaxDescs.hasMore()) {
222 String syntaxDesc = (String)syntaxDescs.next();
223 try {
224 Object[] def = desc2Def(syntaxDesc);
225 currentName = (String) def[0];
226 syntaxDef = (Attributes) def[1];
227 syntaxDefTree.setup(LdapSchemaCtx.SYNTAX,
228 currentName, syntaxDef);
229 } catch (NamingException ne) {
230 // error occurred while parsing, ignore current entry
231 }
232 }
233
234 return syntaxDefTree;
235 }
236
237 final private static DirContext matchRuleDescs2MatchRuleDefs(
238 Attribute matchRuleDescAttr,
239 LdapSchemaCtx schemaRoot)
240 throws NamingException {
241
242 NamingEnumeration matchRuleDescs;
243 Attributes matchRuleDef;
244 LdapSchemaCtx matchRuleDefTree;
245
246 // create the MatchRuleDef subtree
247 Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
248 attrs.put(MATCHRULE_DEF_ATTRS[0], MATCHRULE_DEF_ATTRS[1]);
249 matchRuleDefTree = schemaRoot.setup(LdapSchemaCtx.MATCHRULE_ROOT,
250 MATCHRULE_DEFINITION_NAME, attrs);
251
252 matchRuleDescs = matchRuleDescAttr.getAll();
253 String currentName;
254 while(matchRuleDescs.hasMore()) {
255 String matchRuleDesc = (String)matchRuleDescs.next();
256 try {
257 Object[] def = desc2Def(matchRuleDesc);
258 currentName = (String) def[0];
259 matchRuleDef = (Attributes) def[1];
260 matchRuleDefTree.setup(LdapSchemaCtx.MATCHRULE,
261 currentName, matchRuleDef);
262 } catch (NamingException ne) {
263 // error occurred while parsing, ignore current entry
264 }
265 }
266
267 return matchRuleDefTree;
268 }
269
270 final private static Object[] desc2Def(String desc)
271 throws NamingException {
272 //System.err.println(desc);
273
274 Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
275 Attribute attr = null;
276 int[] pos = new int[]{1}; // tolerate missing leading space
277 boolean moreTags = true;
278
279 // Always begins with <whsp numericoid whsp>
280 attr = readNumericOID(desc, pos);
281 String currentName = (String) attr.get(0); // name is OID by default
282 attrs.put(attr);
283
284 skipWhitespace(desc, pos);
285
286 while (moreTags) {
287 attr = readNextTag(desc, pos);
288 attrs.put(attr);
289
290 if (attr.getID().equals(NAME_ID)) {
291 currentName = (String) attr.get(0); // use NAME attribute as name
292 }
293
294 skipWhitespace(desc, pos);
295
296 if( pos[0] >= desc.length() -1 ) {
297 moreTags = false;
298 }
299 }
300
301 return new Object[] {currentName, attrs};
302 }
303
304 // returns the index of the first whitespace char of a linear whitspace
305 // sequince ending at the given position.
306 final private static int findTrailingWhitespace(String string, int pos) {
307 for(int i = pos; i > 0; i--) {
308 if(string.charAt(i) != WHSP) {
309 return i + 1;
310 }
311 }
312 return 0;
313 }
314
315 final private static void skipWhitespace(String string, int[] pos) {
316 for(int i=pos[0]; i < string.length(); i++) {
317 if(string.charAt(i) != WHSP) {
318 pos[0] = i;
319 if (debug) {
320 System.err.println("skipWhitespace: skipping to "+i);
321 }
322 return;
323 }
324 }
325 }
326
327 final private static Attribute readNumericOID(String string, int[] pos)
328 throws NamingException {
329
330 if (debug) {
331 System.err.println("readNumericoid: pos="+pos[0]);
332 }
333
334 int begin, end;
335 String value = null;
336
337 skipWhitespace(string, pos);
338
339 begin = pos[0];
340 end = string.indexOf(WHSP, begin);
341
342 if (end == -1 || end - begin < 1) {
343 throw new InvalidAttributeValueException("no numericoid found: "
344 + string);
345 }
346
347 value = string.substring(begin, end);
348
349 pos[0] += value.length();
350
351 return new BasicAttribute(NUMERICOID_ID, value);
352 }
353
354 final private static Attribute readNextTag(String string, int[] pos)
355 throws NamingException {
356
357 Attribute attr = null;
358 String tagName = null;
359 String[] values = null;
360
361 skipWhitespace(string, pos);
362
363 if (debug) {
364 System.err.println("readNextTag: pos="+pos[0]);
365 }
366
367 // get the name and values of the attribute to return
368 int trailingSpace = string.indexOf( WHSP, pos[0] );
369
370 // tolerate a schema that omits the trailing space
371 if (trailingSpace < 0) {
372 tagName = string.substring( pos[0], string.length() - 1);
373 } else {
374 tagName = string.substring( pos[0], trailingSpace );
375 }
376
377 values = readTag(tagName, string, pos);
378
379 // make sure at least one value was returned
380 if(values.length < 0) {
381 throw new InvalidAttributeValueException("no values for " +
382 "attribute \"" +
383 tagName + "\"");
384 }
385
386 // create the attribute, using the first value
387 attr = new BasicAttribute(tagName, values[0]);
388
389 // add other values if there are any
390 for(int i = 1; i < values.length; i++) {
391 attr.add(values[i]);
392 }
393
394 return attr;
395 }
396
397 final private static String[] readTag(String tag, String string, int[] pos)
398 throws NamingException {
399
400 if (debug) {
401 System.err.println("ReadTag: " + tag + " pos="+pos[0]);
402 }
403
404 // move parser past tag name
405 pos[0] += tag.length();
406 skipWhitespace(string, pos);
407
408 if (tag.equals(NAME_ID)) {
409 return readQDescrs(string, pos); // names[0] is NAME
410 }
411
412 if(tag.equals(DESC_ID)) {
413 return readQDString(string, pos);
414 }
415
416 if (
417 tag.equals(EQUALITY_ID) ||
418 tag.equals(ORDERING_ID) ||
419 tag.equals(SUBSTR_ID) ||
420 tag.equals(SYNTAX_ID)) {
421 return readWOID(string, pos);
422 }
423
424 if (tag.equals(OBSOLETE_ID) ||
425 tag.equals(ABSTRACT_ID) ||
426 tag.equals(STRUCTURAL_ID) ||
427 tag.equals(AUXILARY_ID) ||
428 tag.equals(SINGLE_VAL_ID) ||
429 tag.equals(COLLECTIVE_ID) ||
430 tag.equals(NO_USER_MOD_ID)) {
431 return new String[] {SCHEMA_TRUE_VALUE};
432 }
433
434 if (tag.equals(SUP_ID) || // oid list for object class; WOID for attribute
435 tag.equals(MUST_ID) ||
436 tag.equals(MAY_ID) ||
437 tag.equals(USAGE_ID)) {
438 return readOIDs(string, pos);
439 }
440
441 // otherwise it's a schema element with a quoted string value
442 return readQDStrings(string, pos);
443 }
444
445 final private static String[] readQDString(String string, int[] pos)
446 throws NamingException {
447
448 int begin, end;
449
450 begin = string.indexOf(SINGLE_QUOTE, pos[0]) + 1;
451 end = string.indexOf(SINGLE_QUOTE, begin);
452
453 if (debug) {
454 System.err.println("ReadQDString: pos=" + pos[0] +
455 " begin=" + begin + " end=" + end);
456 }
457
458 if(begin == -1 || end == -1 || begin == end) {
459 throw new InvalidAttributeIdentifierException("malformed " +
460 "QDString: " +
461 string);
462 }
463
464 // make sure the qdstring end symbol is there
465 if (string.charAt(begin - 1) != SINGLE_QUOTE) {
466 throw new InvalidAttributeIdentifierException("qdstring has " +
467 "no end mark: " +
468 string);
469 }
470
471 pos[0] = end+1;
472 return new String[] {string.substring(begin, end)};
473 }
474
475 /**
476 * dstring = 1*utf8
477 * qdstring = whsp "'" dstring "'" whsp
478 * qdstringlist = [ qdstring *( qdstring ) ]
479 * qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )
480 */
481 private final static String[] readQDStrings(String string, int[] pos)
482 throws NamingException {
483
484 return readQDescrs(string, pos);
485 }
486
487 /**
488 * ; object descriptors used as schema element names
489 * qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp )
490 * qdescrlist = [ qdescr *( qdescr ) ]
491 * qdescr = whsp "'" descr "'" whsp
492 * descr = keystring
493 */
494 final private static String[] readQDescrs(String string, int[] pos)
495 throws NamingException {
496
497 if (debug) {
498 System.err.println("readQDescrs: pos="+pos[0]);
499 }
500
501 skipWhitespace(string, pos);
502
503 switch( string.charAt(pos[0]) ) {
504 case OID_LIST_BEGIN:
505 return readQDescrList(string, pos);
506 case SINGLE_QUOTE:
507 return readQDString(string, pos);
508 default:
509 throw new InvalidAttributeValueException("unexpected oids " +
510 "string: " + string);
511 }
512 }
513
514 /**
515 * qdescrlist = [ qdescr *( qdescr ) ]
516 * qdescr = whsp "'" descr "'" whsp
517 * descr = keystring
518 */
519 final private static String[] readQDescrList(String string, int[] pos)
520 throws NamingException {
521
522 int begin, end;
523 Vector values = new Vector(5);
524
525 if (debug) {
526 System.err.println("ReadQDescrList: pos="+pos[0]);
527 }
528
529 pos[0]++; // skip '('
530 skipWhitespace(string, pos);
531 begin = pos[0];
532 end = string.indexOf(OID_LIST_END, begin);
533
534 if(end == -1) {
535 throw new InvalidAttributeValueException ("oidlist has no end "+
536 "mark: " + string);
537 }
538
539 while(begin < end) {
540 String[] one = readQDString(string, pos);
541
542 if (debug) {
543 System.err.println("ReadQDescrList: found '" + one[0] +
544 "' at begin=" + begin + " end =" + end);
545 }
546
547 values.addElement(one[0]);
548 skipWhitespace(string, pos);
549 begin = pos[0];
550 }
551
552 pos[0] = end+1; // skip ')'
553
554 String[] answer = new String[values.size()];
555 for (int i = 0; i < answer.length; i++) {
556 answer[i] = (String)values.elementAt(i);
557 }
558 return answer;
559 }
560
561 final private static String[] readWOID(String string, int[] pos)
562 throws NamingException {
563
564 if (debug) {
565 System.err.println("readWOIDs: pos="+pos[0]);
566 }
567
568 skipWhitespace(string, pos);
569
570 if (string.charAt(pos[0]) == SINGLE_QUOTE) {
571 // %%% workaround for Netscape schema bug
572 return readQDString(string, pos);
573 }
574
575 int begin, end;
576
577 begin = pos[0];
578 end = string.indexOf(WHSP, begin);
579
580 if (debug) {
581 System.err.println("ReadWOID: pos=" + pos[0] +
582 " begin=" + begin + " end=" + end);
583 }
584
585 if(end == -1 || begin == end) {
586 throw new InvalidAttributeIdentifierException("malformed " +
587 "OID: " +
588 string);
589 }
590 pos[0] = end+1;
591
592 return new String[] {string.substring(begin, end)};
593 }
594
595 /*
596 * oids = woid / ( "(" oidlist ")" )
597 * oidlist = woid *( "$" woid )
598 */
599 final private static String[] readOIDs(String string, int[] pos)
600 throws NamingException {
601
602 if (debug) {
603 System.err.println("readOIDs: pos="+pos[0]);
604 }
605
606 skipWhitespace(string, pos);
607
608 // Single OID
609 if (string.charAt(pos[0]) != OID_LIST_BEGIN) {
610 return readWOID(string, pos);
611 }
612
613 // Multiple OIDs
614
615 int begin, cur, end;
616 String oidName = null;
617 Vector values = new Vector(5);
618
619 if (debug) {
620 System.err.println("ReadOIDList: pos="+pos[0]);
621 }
622
623 pos[0]++;
624 skipWhitespace(string, pos);
625 begin = pos[0];
626 end = string.indexOf(OID_LIST_END, begin);
627 cur = string.indexOf(OID_SEPARATOR, begin);
628
629 if(end == -1) {
630 throw new InvalidAttributeValueException ("oidlist has no end "+
631 "mark: " + string);
632 }
633
634 if(cur == -1 || end < cur) {
635 cur = end;
636 }
637
638 while(cur < end && cur > 0) {
639 int wsBegin = findTrailingWhitespace(string, cur - 1);
640 oidName = string.substring(begin, wsBegin);
641 if (debug) {
642 System.err.println("ReadOIDList: found '" + oidName +
643 "' at begin=" + begin + " end =" + end);
644 }
645 values.addElement(oidName);
646 pos[0] = cur + 1;
647 skipWhitespace(string, pos);
648 begin = pos[0];
649 cur = string.indexOf(OID_SEPARATOR, begin);
650 if(debug) {System.err.println("ReadOIDList: begin = " + begin);}
651 }
652
653 if (debug) {
654 System.err.println("ReadOIDList: found '" + oidName +
655 "' at begin=" + begin + " end =" + end);
656 }
657
658 int wsBegin = findTrailingWhitespace(string, end - 1);
659 oidName = string.substring(begin, wsBegin);
660 values.addElement(oidName);
661
662 pos[0] = end+1;
663
664 String[] answer = new String[values.size()];
665 for (int i = 0; i < answer.length; i++) {
666 answer[i] = (String)values.elementAt(i);
667 }
668 return answer;
669 }
670
671// ----------------- "unparser" methods
672// Methods that are used for translating a node in the schema tree
673// into RFC2252 format for storage back into the LDAP directory
674/*
675 static Attributes JNDI2LDAPSchema(DirContext schemaRoot)
676 throws NamingException {
677
678 Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID);
679 Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID);
680 Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID);
681 Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
682 DirContext classDefs, attributeDefs, syntaxDefs;
683 Attributes classDefsAttrs, attributeDefsAttrs, syntaxDefsAttrs;
684 NamingEnumeration defs;
685 Object obj;
686 int i = 0;
687
688 try {
689 obj = schemaRoot.lookup(OBJECTCLASS_DEFINITION_NAME);
690 if(obj != null && obj instanceof DirContext) {
691 classDefs = (DirContext)obj;
692 defs = classDefs.listBindings("");
693 while(defs.hasMoreElements()) {
694 i++;
695 DirContext classDef = (DirContext)
696 ((Binding)(defs.next())).getObject();
697 classDefAttrs = classDef.getAttributes("");
698 objDescAttr.add(classDef2ObjectDesc(classDefAttrs));
699 }
700 if (debug)
701 System.err.println(i + " total object classes");
702 attrs.put(objDescAttr);
703 } else {
704 throw new NamingException(
705 "Problem with Schema tree: the object named " +
706 OBJECTCLASS_DEFINITION_NAME + " is not a " +
707 "DirContext");
708 }
709 } catch (NameNotFoundException e) {} // ignore
710
711 i=0;
712 try {
713 obj = schemaRoot.lookup(ATTRIBUTE_DEFINITION_NAME);
714 if(obj instanceof DirContext) {
715 attributeDefs = (DirContext)obj;
716 defs = attributeDefs.listBindings("");
717 while(defs.hasMoreElements()) {
718 i++;
719 DirContext attrDef = (DirContext)
720 ((Binding)defs.next()).getObject();
721 attrDefAttrs = attrDef.getAttributes("");
722 attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs));
723 }
724 if (debug)
725 System.err.println(i + " attribute definitions");
726 attrs.put(attrDescAttr);
727 } else {
728 throw new NamingException(
729 "Problem with schema tree: the object named " +
730 ATTRIBUTE_DEFINITION_NAME + " is not a " +
731 "DirContext");
732 }
733 } catch (NameNotFoundException e) {} // ignore
734
735 i=0;
736 try {
737 obj = schemaRoot.lookup(SYNTAX_DEFINITION_NAME);
738 if(obj instanceof DirContext) {
739 syntaxDefs = (DirContext)obj;
740 defs =syntaxDefs.listBindings("");
741 while(defs.hasMoreElements()) {
742 i++;
743 DirContext syntaxDef = (DirContext)
744 ((Binding)defs.next()).getObject();
745 syntaxDefAttrs = syntaxDef.getAttributes("");
746 syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs));
747 }
748 if (debug)
749 System.err.println(i + " total syntax definitions");
750 attrs.put(syntaxDescAttr);
751 } else {
752 throw new NamingException(
753 "Problem with schema tree: the object named " +
754 SYNTAX_DEFINITION_NAME + " is not a " +
755 "DirContext");
756 }
757 } catch (NameNotFoundException e) {} // ignore
758
759 return attrs;
760 }
761
762*/
763
764 /**
765 * Translate attributes that describe an object class into the
766 * string description as defined in RFC 2252.
767 */
768 final private String classDef2ObjectDesc(Attributes attrs)
769 throws NamingException {
770
771 StringBuffer objectDesc = new StringBuffer("( ");
772
773 Attribute attr = null;
774 int count = 0;
775
776 // extract attributes by ID to guarantee ordering
777
778 attr = attrs.get(NUMERICOID_ID);
779 if (attr != null) {
780 objectDesc.append(writeNumericOID(attr));
781 count++;
782 } else {
783 throw new ConfigurationException("Class definition doesn't" +
784 "have a numeric OID");
785 }
786
787 attr = attrs.get(NAME_ID);
788 if (attr != null) {
789 objectDesc.append(writeQDescrs(attr));
790 count++;
791 }
792
793 attr = attrs.get(DESC_ID);
794 if (attr != null) {
795 objectDesc.append(writeQDString(attr));
796 count++;
797 }
798
799 attr = attrs.get(OBSOLETE_ID);
800 if (attr != null) {
801 objectDesc.append(writeBoolean(attr));
802 count++;
803 }
804
805 attr = attrs.get(SUP_ID);
806 if (attr != null) {
807 objectDesc.append(writeOIDs(attr));
808 count++;
809 }
810
811 attr = attrs.get(ABSTRACT_ID);
812 if (attr != null) {
813 objectDesc.append(writeBoolean(attr));
814 count++;
815 }
816
817 attr = attrs.get(STRUCTURAL_ID);
818 if (attr != null) {
819 objectDesc.append(writeBoolean(attr));
820 count++;
821 }
822
823 attr = attrs.get(AUXILARY_ID);
824 if (attr != null) {
825 objectDesc.append(writeBoolean(attr));
826 count++;
827 }
828
829 attr = attrs.get(MUST_ID);
830 if (attr != null) {
831 objectDesc.append(writeOIDs(attr));
832 count++;
833 }
834
835 attr = attrs.get(MAY_ID);
836 if (attr != null) {
837 objectDesc.append(writeOIDs(attr));
838 count++;
839 }
840
841 // process any remaining attributes
842 if (count < attrs.size()) {
843 String attrId = null;
844
845 // use enumeration because attribute ID is not known
846 for (NamingEnumeration ae = attrs.getAll();
847 ae.hasMoreElements(); ) {
848
849 attr = (Attribute)ae.next();
850 attrId = attr.getID();
851
852 // skip those already processed
853 if (attrId.equals(NUMERICOID_ID) ||
854 attrId.equals(NAME_ID) ||
855 attrId.equals(SUP_ID) ||
856 attrId.equals(MAY_ID) ||
857 attrId.equals(MUST_ID) ||
858 attrId.equals(STRUCTURAL_ID) ||
859 attrId.equals(DESC_ID) ||
860 attrId.equals(AUXILARY_ID) ||
861 attrId.equals(ABSTRACT_ID) ||
862 attrId.equals(OBSOLETE_ID)) {
863 continue;
864
865 } else {
866 objectDesc.append(writeQDStrings(attr));
867 }
868 }
869 }
870
871 objectDesc.append(")");
872
873 return objectDesc.toString();
874 }
875
876 /**
877 * Translate attributes that describe an attribute definition into the
878 * string description as defined in RFC 2252.
879 */
880 final private String attrDef2AttrDesc(Attributes attrs)
881 throws NamingException {
882
883 StringBuffer attrDesc = new StringBuffer("( "); // opening parens
884
885 Attribute attr = null;
886 int count = 0;
887
888 // extract attributes by ID to guarantee ordering
889
890 attr = attrs.get(NUMERICOID_ID);
891 if (attr != null) {
892 attrDesc.append(writeNumericOID(attr));
893 count++;
894 } else {
895 throw new ConfigurationException("Attribute type doesn't" +
896 "have a numeric OID");
897 }
898
899 attr = attrs.get(NAME_ID);
900 if (attr != null) {
901 attrDesc.append(writeQDescrs(attr));
902 count++;
903 }
904
905 attr = attrs.get(DESC_ID);
906 if (attr != null) {
907 attrDesc.append(writeQDString(attr));
908 count++;
909 }
910
911 attr = attrs.get(OBSOLETE_ID);
912 if (attr != null) {
913 attrDesc.append(writeBoolean(attr));
914 count++;
915 }
916
917 attr = attrs.get(SUP_ID);
918 if (attr != null) {
919 attrDesc.append(writeWOID(attr));
920 count++;
921 }
922
923 attr = attrs.get(EQUALITY_ID);
924 if (attr != null) {
925 attrDesc.append(writeWOID(attr));
926 count++;
927 }
928
929 attr = attrs.get(ORDERING_ID);
930 if (attr != null) {
931 attrDesc.append(writeWOID(attr));
932 count++;
933 }
934
935 attr = attrs.get(SUBSTR_ID);
936 if (attr != null) {
937 attrDesc.append(writeWOID(attr));
938 count++;
939 }
940
941 attr = attrs.get(SYNTAX_ID);
942 if (attr != null) {
943 attrDesc.append(writeWOID(attr));
944 count++;
945 }
946
947 attr = attrs.get(SINGLE_VAL_ID);
948 if (attr != null) {
949 attrDesc.append(writeBoolean(attr));
950 count++;
951 }
952
953 attr = attrs.get(COLLECTIVE_ID);
954 if (attr != null) {
955 attrDesc.append(writeBoolean(attr));
956 count++;
957 }
958
959 attr = attrs.get(NO_USER_MOD_ID);
960 if (attr != null) {
961 attrDesc.append(writeBoolean(attr));
962 count++;
963 }
964
965 attr = attrs.get(USAGE_ID);
966 if (attr != null) {
967 attrDesc.append(writeQDString(attr));
968 count++;
969 }
970
971 // process any remaining attributes
972 if (count < attrs.size()) {
973 String attrId = null;
974
975 // use enumeration because attribute ID is not known
976 for (NamingEnumeration ae = attrs.getAll();
977 ae.hasMoreElements(); ) {
978
979 attr = (Attribute)ae.next();
980 attrId = attr.getID();
981
982 // skip those already processed
983 if (attrId.equals(NUMERICOID_ID) ||
984 attrId.equals(NAME_ID) ||
985 attrId.equals(SYNTAX_ID) ||
986 attrId.equals(DESC_ID) ||
987 attrId.equals(SINGLE_VAL_ID) ||
988 attrId.equals(EQUALITY_ID) ||
989 attrId.equals(ORDERING_ID) ||
990 attrId.equals(SUBSTR_ID) ||
991 attrId.equals(NO_USER_MOD_ID) ||
992 attrId.equals(USAGE_ID) ||
993 attrId.equals(SUP_ID) ||
994 attrId.equals(COLLECTIVE_ID) ||
995 attrId.equals(OBSOLETE_ID)) {
996 continue;
997
998 } else {
999 attrDesc.append(writeQDStrings(attr));
1000 }
1001 }
1002 }
1003
1004 attrDesc.append(")"); // add closing parens
1005
1006 return attrDesc.toString();
1007 }
1008
1009 /**
1010 * Translate attributes that describe an attribute syntax definition into the
1011 * string description as defined in RFC 2252.
1012 */
1013 final private String syntaxDef2SyntaxDesc(Attributes attrs)
1014 throws NamingException {
1015
1016 StringBuffer syntaxDesc = new StringBuffer("( "); // opening parens
1017
1018 Attribute attr = null;
1019 int count = 0;
1020
1021 // extract attributes by ID to guarantee ordering
1022
1023 attr = attrs.get(NUMERICOID_ID);
1024 if (attr != null) {
1025 syntaxDesc.append(writeNumericOID(attr));
1026 count++;
1027 } else {
1028 throw new ConfigurationException("Attribute type doesn't" +
1029 "have a numeric OID");
1030 }
1031
1032 attr = attrs.get(DESC_ID);
1033 if (attr != null) {
1034 syntaxDesc.append(writeQDString(attr));
1035 count++;
1036 }
1037
1038 // process any remaining attributes
1039 if (count < attrs.size()) {
1040 String attrId = null;
1041
1042 // use enumeration because attribute ID is not known
1043 for (NamingEnumeration ae = attrs.getAll();
1044 ae.hasMoreElements(); ) {
1045
1046 attr = (Attribute)ae.next();
1047 attrId = attr.getID();
1048
1049 // skip those already processed
1050 if (attrId.equals(NUMERICOID_ID) ||
1051 attrId.equals(DESC_ID)) {
1052 continue;
1053
1054 } else {
1055 syntaxDesc.append(writeQDStrings(attr));
1056 }
1057 }
1058 }
1059
1060 syntaxDesc.append(")");
1061
1062 return syntaxDesc.toString();
1063 }
1064
1065 /**
1066 * Translate attributes that describe an attribute matching rule
1067 * definition into the string description as defined in RFC 2252.
1068 */
1069 final private String matchRuleDef2MatchRuleDesc(Attributes attrs)
1070 throws NamingException {
1071
1072 StringBuffer matchRuleDesc = new StringBuffer("( "); // opening parens
1073
1074 Attribute attr = null;
1075 int count = 0;
1076
1077 // extract attributes by ID to guarantee ordering
1078
1079 attr = attrs.get(NUMERICOID_ID);
1080 if (attr != null) {
1081 matchRuleDesc.append(writeNumericOID(attr));
1082 count++;
1083 } else {
1084 throw new ConfigurationException("Attribute type doesn't" +
1085 "have a numeric OID");
1086 }
1087
1088 attr = attrs.get(NAME_ID);
1089 if (attr != null) {
1090 matchRuleDesc.append(writeQDescrs(attr));
1091 count++;
1092 }
1093
1094 attr = attrs.get(DESC_ID);
1095 if (attr != null) {
1096 matchRuleDesc.append(writeQDString(attr));
1097 count++;
1098 }
1099
1100 attr = attrs.get(OBSOLETE_ID);
1101 if (attr != null) {
1102 matchRuleDesc.append(writeBoolean(attr));
1103 count++;
1104 }
1105
1106 attr = attrs.get(SYNTAX_ID);
1107 if (attr != null) {
1108 matchRuleDesc.append(writeWOID(attr));
1109 count++;
1110 } else {
1111 throw new ConfigurationException("Attribute type doesn't" +
1112 "have a syntax OID");
1113 }
1114
1115 // process any remaining attributes
1116 if (count < attrs.size()) {
1117 String attrId = null;
1118
1119 // use enumeration because attribute ID is not known
1120 for (NamingEnumeration ae = attrs.getAll();
1121 ae.hasMoreElements(); ) {
1122
1123 attr = (Attribute)ae.next();
1124 attrId = attr.getID();
1125
1126 // skip those already processed
1127 if (attrId.equals(NUMERICOID_ID) ||
1128 attrId.equals(NAME_ID) ||
1129 attrId.equals(SYNTAX_ID) ||
1130 attrId.equals(DESC_ID) ||
1131 attrId.equals(OBSOLETE_ID)) {
1132 continue;
1133
1134 } else {
1135 matchRuleDesc.append(writeQDStrings(attr));
1136 }
1137 }
1138 }
1139
1140 matchRuleDesc.append(")");
1141
1142 return matchRuleDesc.toString();
1143 }
1144
1145 final private String writeNumericOID(Attribute nOIDAttr)
1146 throws NamingException {
1147 if(nOIDAttr.size() != 1) {
1148 throw new InvalidAttributeValueException(
1149 "A class definition must have exactly one numeric OID");
1150 }
1151 return (String)(nOIDAttr.get()) + WHSP;
1152 }
1153
1154 final private String writeWOID(Attribute attr) throws NamingException {
1155 if (netscapeBug)
1156 return writeQDString(attr);
1157 else
1158 return attr.getID() + WHSP + attr.get() + WHSP;
1159 }
1160
1161 /* qdescr = whsp "'" descr "'" whsp */
1162 final private String writeQDString(Attribute qdStringAttr)
1163 throws NamingException {
1164 if(qdStringAttr.size() != 1) {
1165 throw new InvalidAttributeValueException(
1166 qdStringAttr.getID() + " must have exactly one value");
1167 }
1168
1169 return qdStringAttr.getID() + WHSP +
1170 SINGLE_QUOTE + qdStringAttr.get() + SINGLE_QUOTE + WHSP;
1171 }
1172
1173 /**
1174 * dstring = 1*utf8
1175 * qdstring = whsp "'" dstring "'" whsp
1176 * qdstringlist = [ qdstring *( qdstring ) ]
1177 * qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )
1178 */
1179 private final String writeQDStrings(Attribute attr) throws NamingException {
1180 return writeQDescrs(attr);
1181 }
1182
1183 /**
1184 * qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp )
1185 * qdescrlist = [ qdescr *( qdescr ) ]
1186 * qdescr = whsp "'" descr "'" whsp
1187 * descr = keystring
1188 */
1189 private final String writeQDescrs(Attribute attr) throws NamingException {
1190 switch(attr.size()) {
1191 case 0:
1192 throw new InvalidAttributeValueException(
1193 attr.getID() + "has no values");
1194 case 1:
1195 return writeQDString(attr);
1196 }
1197
1198 // write QDList
1199
1200 StringBuffer qdList = new StringBuffer(attr.getID());
1201 qdList.append(WHSP);
1202 qdList.append(OID_LIST_BEGIN);
1203
1204 NamingEnumeration values = attr.getAll();
1205
1206 while(values.hasMore()) {
1207 qdList.append(WHSP);
1208 qdList.append(SINGLE_QUOTE);
1209 qdList.append((String)values.next());
1210 qdList.append(SINGLE_QUOTE);
1211 qdList.append(WHSP);
1212 }
1213
1214 qdList.append(OID_LIST_END);
1215 qdList.append(WHSP);
1216
1217 return qdList.toString();
1218 }
1219
1220 final private String writeOIDs(Attribute oidsAttr)
1221 throws NamingException {
1222
1223 switch(oidsAttr.size()) {
1224 case 0:
1225 throw new InvalidAttributeValueException(
1226 oidsAttr.getID() + "has no values");
1227
1228 case 1:
1229 if (netscapeBug) {
1230 break; // %%% write out as list to avoid crashing server
1231 }
1232 return writeWOID(oidsAttr);
1233 }
1234
1235 // write OID List
1236
1237 StringBuffer oidList = new StringBuffer(oidsAttr.getID());
1238 oidList.append(WHSP);
1239 oidList.append(OID_LIST_BEGIN);
1240
1241 NamingEnumeration values = oidsAttr.getAll();
1242 oidList.append(WHSP);
1243 oidList.append(values.next());
1244
1245 while(values.hasMore()) {
1246 oidList.append(WHSP);
1247 oidList.append(OID_SEPARATOR);
1248 oidList.append(WHSP);
1249 oidList.append((String)values.next());
1250 }
1251
1252 oidList.append(WHSP);
1253 oidList.append(OID_LIST_END);
1254 oidList.append(WHSP);
1255
1256 return oidList.toString();
1257 }
1258
1259 private final String writeBoolean(Attribute booleanAttr)
1260 throws NamingException {
1261 return booleanAttr.getID() + WHSP;
1262 }
1263
1264 /**
1265 * Returns an attribute for updating the Object Class Definition schema
1266 * attribute
1267 */
1268 final Attribute stringifyObjDesc(Attributes classDefAttrs)
1269 throws NamingException {
1270 Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID);
1271 objDescAttr.add(classDef2ObjectDesc(classDefAttrs));
1272 return objDescAttr;
1273 }
1274
1275 /**
1276 * Returns an attribute for updating the Attribute Definition schema attribute
1277 */
1278 final Attribute stringifyAttrDesc(Attributes attrDefAttrs)
1279 throws NamingException {
1280 Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID);
1281 attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs));
1282 return attrDescAttr;
1283 }
1284
1285 /**
1286 * Returns an attribute for updating the Syntax schema attribute
1287 */
1288 final Attribute stringifySyntaxDesc(Attributes syntaxDefAttrs)
1289 throws NamingException {
1290 Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID);
1291 syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs));
1292 return syntaxDescAttr;
1293 }
1294
1295 /**
1296 * Returns an attribute for updating the Matching Rule schema attribute
1297 */
1298 final Attribute stringifyMatchRuleDesc(Attributes matchRuleDefAttrs)
1299 throws NamingException {
1300 Attribute matchRuleDescAttr = new BasicAttribute(MATCHRULEDESC_ATTR_ID);
1301 matchRuleDescAttr.add(matchRuleDef2MatchRuleDesc(matchRuleDefAttrs));
1302 return matchRuleDescAttr;
1303 }
1304}