1 /* 2 * Copyright (c) 1999, 2004, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import javax.naming.*; 29 import javax.naming.directory.*; 30 import java.util.Hashtable; 31 import 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 40 final 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 }