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