1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1999 by Sun Microsystems, Inc. 23 * All rights reserved. 24 * 25 */ 26 27 // ServiceLocationAttributeV1.java: SLPv1 character encoding and decoding 28 // Author: James Kempf 29 // Created On: Fri Oct 9 19:18:17 1998 30 // Last Modified By: James Kempf 31 // Last Modified On: Sat Oct 24 13:17:58 1998 32 // Update Count: 15 33 // 34 35 package com.sun.slp; 36 37 import java.util.*; 38 39 /** 40 * Handles attribute string encoding and decoding for SLPv1. 41 * 42 * @author James Kempf 43 */ 44 45 class ServiceLocationAttributeV1 extends ServiceLocationAttribute { 46 47 String charCode = IANACharCode.UTF8; // how to encode the attribute. 48 49 // Characters to escape. 50 51 final private static String UNESCAPABLE_CHARS = ",=!></*()"; 52 final private static String ESCAPABLE_CHARS = 53 UNESCAPABLE_CHARS + "&#;"; 54 55 /** 56 * Handles radix64 string encoding and decoding for SLPv1. 57 * 58 * @author James Kempf 59 */ 60 61 static class Radix64 extends Object { 62 63 /** 64 * Translates the 6 bit value to the corresponding radix 64 65 * representation. 66 */ 67 private static char LUT(char cin) { 68 69 int i = (int)(cin & (char)0x00FF); 70 char result = ' '; 71 72 if (i < 26) { 73 result = (char)((char)i + 'A'); 74 75 } else if (i < 52) { 76 result = (char)((char)(i - 26) + 'a'); 77 78 } else if (i < 62) { 79 result = (char)((char)(i - 52) + '0'); 80 81 } else if (i == 62) { 82 result = '+'; 83 84 } else if (i == 63) { 85 result = '/'; 86 87 } 88 89 return result; 90 } 91 92 /** 93 * Translates a radix 64 representation to the 64 bit value which 94 * corresponds to it. 95 */ 96 private static char LUT2(char cin, String s) 97 throws ServiceLocationException { 98 99 int i = (int)(cin & 0x00ff); 100 char c = (char) 0xffff; 101 102 if (((char)i >= 'A') && ((char)i <= 'Z')) { 103 c = (char)((char)i - 'A'); 104 105 } 106 107 if (((char)i >= 'a') && ((char)i <= 'z')) { 108 c = (char)((char)i - 'a' +(char) 26); 109 110 } 111 112 if (((char)i >= '0') && ((char)i <= '9')) { 113 c = (char)((char)i - '0' +(char) 52); 114 115 } 116 117 if ((char)i == '+') { 118 c = (char)62; 119 120 } 121 122 if ((char)i == '/') { 123 c = (char)63; 124 125 } 126 127 if ((char)i == '=') { 128 c = (char)0; 129 130 } 131 132 if (c == 0xffff) { 133 throw 134 new ServiceLocationException( 135 ServiceLocationException.PARSE_ERROR, 136 "v1_radix64_error", 137 new Object[] {s}); 138 139 } 140 141 return c; 142 } 143 144 // format of the encoding is "(###:encoding)" where ### is the length 145 146 // convert a string in the encoding to the buffer format 147 148 static Opaque radix64ToOpaque(String s) 149 throws ServiceLocationException { 150 151 if (s == null || s.trim().length() == 0) { 152 return new Opaque(new byte[0]); 153 154 } 155 156 int oplen = 0; 157 int scan = 0; 158 159 while (scan < s.length()) { 160 if (s.charAt(scan) == '(') { 161 break; // scan till begins 162 163 } 164 165 scan++; 166 } 167 168 scan++; // past the '(' 169 170 while (scan < s.length()) { 171 if (Character.isWhitespace(s.charAt(scan)) == false) { 172 break; 173 174 } 175 scan++; 176 } 177 178 while (scan < s.length()) { 179 180 if (Character.isDigit(s.charAt(scan))) { 181 oplen *= 10; 182 oplen += (s.charAt(scan) - '0'); 183 scan++; 184 185 } else { 186 break; 187 188 } 189 } 190 191 if (scan >= s.length()) { 192 throw 193 new ServiceLocationException( 194 ServiceLocationException.PARSE_ERROR, 195 "v1_radix64_error", 196 new Object[] {s}); 197 198 } 199 200 201 if (s.charAt(scan) != ':') { 202 throw 203 new ServiceLocationException( 204 ServiceLocationException.PARSE_ERROR, 205 "v1_radix64_error", 206 new Object[] {s}); 207 208 } 209 210 scan++; // past the ':' 211 212 byte b[] = new byte[oplen]; 213 214 int pos = 0; 215 int timesthrough = (oplen/3); 216 217 if ((oplen %3) != 0) { 218 timesthrough++; 219 220 } 221 222 for (int i = 0; i < timesthrough; i++) { 223 224 // get 4 bytes to make 3 with, skipping blanks 225 226 char v[] = new char[4]; 227 228 for (int x = 0; x < 4; x++) { 229 230 while ((scan < s.length()) && 231 Character.isWhitespace(s.charAt(scan))) { 232 scan++; // eat white 233 234 } 235 236 if (scan >= s.length()) { 237 throw 238 new ServiceLocationException( 239 ServiceLocationException.PARSE_ERROR, 240 "v1_radix64_error", 241 new Object[] {s}); 242 243 } 244 245 v[x] = LUT2(s.charAt(scan), s); 246 scan++; 247 } 248 249 b[pos++] = 250 (byte) (((0x3F & v[0]) << 2) + ((0x30 & v[1]) >> 4)); 251 if (pos >= oplen) break; 252 b[pos++] = 253 (byte) (((0x0F & v[1]) << 4) + ((0x3C & v[2]) >> 2)); 254 if (pos >= oplen) break; 255 b[pos++] = (byte) (((0x03 & v[2]) << 6) + (0x3F & v[3])); 256 257 } // end of conversion loop 258 259 if (scan >= s.length()) { 260 throw 261 new ServiceLocationException( 262 ServiceLocationException.PARSE_ERROR, 263 "v1_radix64_error", 264 new Object[] {s}); 265 } 266 267 if (s.charAt(scan) != ')') {// check for too many chars. 268 throw 269 new ServiceLocationException( 270 ServiceLocationException.PARSE_ERROR, 271 "v1_radix64_error", 272 new Object[] {s}); 273 274 } 275 276 return new Opaque(b); 277 } 278 279 // convert an Opaque to the encoding 280 281 static String opaqueToRadix64(Opaque oq) { 282 byte[] b = oq.bytes; 283 284 if (b == null) { 285 return new String(""); 286 287 } 288 289 StringBuffer sb = new StringBuffer("("+b.length+":"); 290 291 int datalen; 292 int fill = b.length%3; 293 294 if (fill == 0) { 295 datalen = (b.length / 3) * 4; 296 297 } else { 298 datalen = ((b.length / 3) + 1) * 4; 299 300 } 301 302 int dataoffset = 0; 303 int more = (b.length%3); 304 305 if (more != 0) { 306 more = 1; 307 308 } 309 310 int a[] = new int[4]; 311 312 for (int i = 0; i < ((b.length/3)+more-1); i++) { 313 314 a[0] = (int)(0xFC & (char)b[ dataoffset ]) >> 2; 315 a[1] = ((int)(0x03 & (char)b[ dataoffset ]) << 4) + 316 ((int)(0xF0 & (char)b[ dataoffset + 1]) >> 4); 317 a[2] = ((int)(0x0F & (char)b[ dataoffset + 1]) << 2) + 318 ((int)(0xC0 & (char)b[ dataoffset + 2]) >> 6); 319 a[3] = (int)(0x3F & (char)b[ dataoffset + 2]); 320 321 for (int j = 0; j < 4; j++) { 322 sb.append(LUT((char)a[j])); 323 324 } 325 326 dataoffset += 3; 327 } 328 329 byte f1 = 0, f2 = 0; 330 331 if (fill == 0) { 332 f1 = b[ dataoffset + 1 ]; 333 f2 = b[ dataoffset + 2 ]; 334 335 } else if (fill == 2) { 336 f1 = b[ dataoffset + 1 ]; 337 338 } 339 340 a[0] = (int) (0xFC & (char)b[ dataoffset ]) >> 2; 341 a[1] = ((int) (0x03 & (char)b[ dataoffset ]) << 4) + 342 ((int) (0xF0 & (char)f1) >> 4); 343 a[2] = ((int) (0x0F & (char)f1) << 2) + 344 ((int) (0xC0 & (char)f2) >> 6); 345 a[3] = (int) (0x3F & (char)f2); 346 347 for (int j = 0; j < 4; j++) { 348 sb.append(LUT((char) a[j])); 349 350 } 351 352 sb.append(")"); 353 354 return sb.toString(); 355 } 356 } 357 358 // Create an SLPv1 attribute from a general attribute. 359 360 ServiceLocationAttributeV1(ServiceLocationAttribute attr) { 361 id = attr.id; 362 values = attr.values; 363 364 } 365 366 // Create an SLPv1 attribute from the parenthesized expression, using 367 // charCode to decode any encodings. 368 369 ServiceLocationAttributeV1(String exp, 370 String charCode, 371 boolean allowMultiValuedBooleans) 372 throws ServiceLocationException { 373 this.charCode = charCode; 374 375 // If start and end paren, then parse out assignment. 376 377 if (exp.startsWith("(") && exp.endsWith(")")) { 378 379 StringTokenizer tk = 380 new StringTokenizer(exp.substring(1, exp.length() - 1), 381 "=", 382 true); 383 384 try { 385 386 // Get the tag. 387 388 id = 389 unescapeAttributeString(tk.nextToken(), charCode); 390 391 if (id.length() <= 0) { 392 throw 393 new ServiceLocationException( 394 ServiceLocationException.PARSE_ERROR, 395 "null_id", 396 new Object[] {exp}); 397 } 398 399 tk.nextToken(); // get rid of "=" 400 401 // Gather the rest. 402 403 String rest = tk.nextToken(""); 404 405 // Parse the comma separated list. 406 407 values = SrvLocHeader.parseCommaSeparatedListIn(rest, true); 408 409 // Convert to objects. 410 411 int i, n = values.size(); 412 Class vecClass = null; 413 414 for (i = 0; i < n; i++) { 415 String value = (String)values.elementAt(i); 416 417 // Need to determine which type to use. 418 419 Object o = evaluate(value, charCode); 420 421 // Convert Opaque to byte array. 422 423 if (o instanceof Opaque) { 424 o = ((Opaque)o).bytes; 425 426 } 427 428 values.setElementAt(o, i); 429 430 } 431 432 } catch (NoSuchElementException ex) { 433 throw 434 new ServiceLocationException( 435 ServiceLocationException.PARSE_ERROR, 436 "assignment_syntax_err", 437 new Object[] {exp}); 438 } 439 440 verifyValueTypes(values, allowMultiValuedBooleans); 441 442 } else { 443 444 // Check to make sure there's no parens. 445 446 if (exp.indexOf('(') != -1 || exp.indexOf(')') != -1) { 447 throw 448 new ServiceLocationException( 449 ServiceLocationException.PARSE_ERROR, 450 "assignment_syntax_err", 451 new Object[] {exp}); 452 } 453 454 // Unescape the keyword. 455 456 id = unescapeAttributeString(exp, charCode); 457 458 } 459 } 460 461 // Duplicate of the one in ServiceLocatioAttribute, except we use our 462 // unescapeAttributeString. 463 464 static Object evaluate(String value, String charCode) 465 throws ServiceLocationException { 466 467 Object o = null; 468 469 // If it can be converted into an integer, then convert it. 470 471 try { 472 473 o = Integer.valueOf(value); 474 475 } catch (NumberFormatException ex) { 476 477 // Wasn't an integer. Try boolean. 478 479 if (value.equalsIgnoreCase(TRUE) || 480 value.equalsIgnoreCase(FALSE)) { 481 o = Boolean.valueOf(value); 482 483 } else { 484 485 // Process the string to remove escapes. 486 487 String val = (String)value; 488 489 // If it begins with the opaque prefix, treat it as an 490 // opaque. Use radix64 parser to convert. 491 492 if (val.startsWith("(")) { 493 o = Radix64.radix64ToOpaque(val); 494 495 } else { 496 o = unescapeAttributeString(val, charCode); 497 498 } 499 } 500 } 501 502 return o; 503 504 } 505 506 // Externalize the attribute, using its charCode to encode any reserved 507 // characters. 508 509 String externalize() 510 throws ServiceLocationException { 511 512 if (values == null) { // keyword attribute... 513 return escapeAttributeString(id, charCode); 514 } 515 516 Vector v = new Vector(); 517 518 for (Enumeration e = values.elements(); e.hasMoreElements(); ) { 519 Object o = e.nextElement(); 520 String s = null; 521 522 s = escapeValueInternal(o, charCode); 523 524 v.addElement(s); 525 } 526 527 StringBuffer buf = 528 new StringBuffer("(" + 529 escapeAttributeString(id, charCode) + 530 "="); 531 532 buf.append(SrvLocHeader.vectorToCommaSeparatedList(v)); 533 534 buf.append(")"); 535 536 return buf.toString(); 537 } 538 539 // Exactly like the one in ServiceLocationAttribute, but use our 540 // escapeAttributeString. 541 542 private static String escapeValueInternal(Object val, String charCode) { 543 544 String s; 545 546 // Escape any characters needing it. 547 548 if (val instanceof String) { 549 550 try { 551 552 s = escapeAttributeString((String)val, charCode); 553 554 } catch (ServiceLocationException ex) { 555 throw 556 new IllegalArgumentException(ex.getMessage()); 557 558 } 559 560 } else if (val instanceof Opaque) { 561 562 // Convert to radix 64. 563 564 s = Radix64.opaqueToRadix64((Opaque)val); 565 566 } else { 567 s = val.toString(); 568 569 } 570 571 return s; 572 } 573 574 // Escape an attribute string with the char code. 575 576 static String escapeAttributeString(String string, 577 String charCode) 578 throws ServiceLocationException { 579 580 StringBuffer buf = new StringBuffer(); 581 int i, n = string.length(); 582 boolean is8bit = 583 (charCode.equals(IANACharCode.ASCII) || 584 charCode.equals(IANACharCode.LATIN1)); 585 586 for (i = 0; i < n; i++) { 587 char c = string.charAt(i); 588 589 if (ESCAPABLE_CHARS.indexOf(c) != -1) { 590 591 buf.append("&#"); 592 buf.append(IANACharCode.escapeChar(c, charCode)); 593 buf.append(";"); 594 595 } else { 596 597 // Need to check ASCII and LATIN1 to make sure that 598 // the character is not outside their range of 599 // representation. 600 601 if (is8bit && (short)c > 255) { 602 throw 603 new ServiceLocationException( 604 ServiceLocationException.PARSE_ERROR, 605 "v1_8bit_error", 606 new Object[] {new Character(c)}); 607 } 608 609 buf.append(c); 610 611 } 612 } 613 614 return buf.toString(); 615 } 616 617 // Unescape attribute string, using charCode for reserved characters. 618 619 static String unescapeAttributeString(String string, 620 String charCode) 621 throws ServiceLocationException { 622 623 // Process escapes. 624 625 int i, n = string.length(); 626 StringBuffer buf = new StringBuffer(n); 627 628 for (i = 0; i < n; i++) { 629 char c = string.charAt(i); 630 631 // Check for invalids. 632 633 int idx = -1; 634 635 if ((idx = UNESCAPABLE_CHARS.indexOf(c)) != -1) { 636 throw 637 new ServiceLocationException( 638 ServiceLocationException.PARSE_ERROR, 639 "v1_escape_error", 640 new Object[] {string}); 641 } 642 643 // Check for escapes. 644 645 if (c != '&') { 646 647 buf.append(c); 648 649 } else { 650 651 // Check to be sure we've got enough characters left. We need 652 // at least 3. 653 654 if ((i + 1) >= n) { 655 throw 656 new ServiceLocationException( 657 ServiceLocationException.PARSE_ERROR, 658 "v1_escape_error", 659 new Object[] {string}); 660 } 661 662 c = string.charAt(++i); 663 664 if (c != '#') { 665 throw 666 new ServiceLocationException( 667 ServiceLocationException.PARSE_ERROR, 668 "v1_escape_error", 669 new Object[] {string}); 670 } 671 672 // Iterate through numbers, collecting. 673 674 StringBuffer num = new StringBuffer(n); 675 676 for (i++; i < n; i++) { 677 678 c = string.charAt(i); 679 680 if (!Character.isDigit(c)) { 681 break; 682 } 683 684 num.append(c); 685 } 686 687 // If the buffer is empty, then throw exception 688 689 if (num.length() <= 0) { 690 throw 691 new ServiceLocationException( 692 ServiceLocationException.PARSE_ERROR, 693 "v1_escape_error", 694 new Object[] {string}); 695 } 696 697 // If the last one isn't ";", we've got a problem. 698 699 if (c != ';') { 700 throw 701 new ServiceLocationException( 702 ServiceLocationException.PARSE_ERROR, 703 "v1_escape_error", 704 new Object[] {string}); 705 } 706 707 // OK, now convert to a character and add to buffer. 708 709 try { 710 buf.append(IANACharCode.unescapeChar(num.toString(), 711 charCode)); 712 713 } catch (NumberFormatException ex) { 714 715 throw 716 new ServiceLocationException( 717 ServiceLocationException.PARSE_ERROR, 718 "v1_escape_error", 719 new Object[] {string}); 720 } 721 } 722 } 723 724 return buf.toString(); 725 } 726 } 727