1 /* 2 * Copyright (c) 1998, 2006, 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.security.auth; 27 28 import java.io.*; 29 import java.lang.RuntimePermission; 30 import java.net.MalformedURLException; 31 import java.net.SocketPermission; 32 import java.net.URL; 33 import java.util.Enumeration; 34 import java.util.Hashtable; 35 import java.util.LinkedList; 36 import java.util.ListIterator; 37 import java.util.Vector; 38 import java.util.StringTokenizer; 39 import java.security.GeneralSecurityException; 40 import sun.security.util.PropertyExpander; 41 42 /** 43 * The policy for a Java runtime (specifying 44 * which permissions are available for code from various principals) 45 * is represented as a separate 46 * persistent configuration. The configuration may be stored as a 47 * flat ASCII file, as a serialized binary file of 48 * the Policy class, or as a database. <p> 49 * 50 * <p>The Java runtime creates one global Policy object, which is used to 51 * represent the static policy configuration file. It is consulted by 52 * a ProtectionDomain when the protection domain initializes its set of 53 * permissions. <p> 54 * 55 * <p>The Policy <code>init</code> method parses the policy 56 * configuration file, and then 57 * populates the Policy object. The Policy object is agnostic in that 58 * it is not involved in making policy decisions. It is merely the 59 * Java runtime representation of the persistent policy configuration 60 * file. <p> 61 * 62 * <p>When a protection domain needs to initialize its set of 63 * permissions, it executes code such as the following 64 * to ask the global Policy object to populate a 65 * Permissions object with the appropriate permissions: 66 * <pre> 67 * policy = Policy.getPolicy(); 68 * Permissions perms = policy.getPermissions(MyCodeSource) 69 * </pre> 70 * 71 * <p>The protection domain passes in a CodeSource 72 * object, which encapsulates its codebase (URL) and public key attributes. 73 * The Policy object evaluates the global policy in light of who the 74 * principal is and returns an appropriate Permissions object. 75 * 76 * @deprecated As of JDK 1.4, replaced by 77 * {@link sun.security.provider.PolicyParser}. 78 * This class is entirely deprecated. 79 * 80 * @author Roland Schemers 81 * 82 * @since 1.2 83 */ 84 @Deprecated 85 class PolicyParser { 86 87 private static final java.util.ResourceBundle rb = 88 java.security.AccessController.doPrivileged 89 (new java.security.PrivilegedAction<java.util.ResourceBundle>() { 90 public java.util.ResourceBundle run() { 91 return (java.util.ResourceBundle.getBundle 92 ("sun.security.util.AuthResources")); 93 } 94 }); 95 96 private Vector<GrantEntry> grantEntries; 97 98 // Convenience variables for parsing 99 private static final sun.security.util.Debug debug = 100 sun.security.util.Debug.getInstance("parser", "\t[Auth Policy Parser]"); 101 private StreamTokenizer st; 102 private int lookahead; 103 private int linenum; 104 private boolean expandProp = false; 105 private String keyStoreUrlString = null; // unexpanded 106 private String keyStoreType = null; 107 108 private String expand(String value) 109 throws PropertyExpander.ExpandException 110 { 111 if (expandProp) 112 return PropertyExpander.expand(value); 113 else 114 return value; 115 } 116 /** 117 * Creates a PolicyParser object. 118 */ 119 120 public PolicyParser() { 121 grantEntries = new Vector<GrantEntry>(); 122 } 123 124 125 public PolicyParser(boolean expandProp) { 126 this(); 127 this.expandProp = expandProp; 128 } 129 130 /** 131 * Reads a policy configuration into the Policy object using a 132 * Reader object. <p> 133 * 134 * @param policy the policy Reader object. 135 * 136 * @exception ParsingException if the policy configuration contains 137 * a syntax error. 138 * 139 * @exception IOException if an error occurs while reading the policy 140 * configuration. 141 */ 142 143 public void read(Reader policy) 144 throws ParsingException, IOException 145 { 146 if (!(policy instanceof BufferedReader)) { 147 policy = new BufferedReader(policy); 148 } 149 150 /** 151 * Configure the stream tokenizer: 152 * Recognize strings between "..." 153 * Don't convert words to lowercase 154 * Recognize both C-style and C++-style comments 155 * Treat end-of-line as white space, not as a token 156 */ 157 st = new StreamTokenizer(policy); 158 159 st.resetSyntax(); 160 st.wordChars('a', 'z'); 161 st.wordChars('A', 'Z'); 162 st.wordChars('.', '.'); 163 st.wordChars('0', '9'); 164 st.wordChars('_', '_'); 165 st.wordChars('$', '$'); 166 st.wordChars(128 + 32, 255); 167 st.whitespaceChars(0, ' '); 168 st.commentChar('/'); 169 st.quoteChar('\''); 170 st.quoteChar('"'); 171 st.lowerCaseMode(false); 172 st.ordinaryChar('/'); 173 st.slashSlashComments(true); 174 st.slashStarComments(true); 175 176 /** 177 * The main parsing loop. The loop is executed once 178 * for each entry in the config file. The entries 179 * are delimited by semicolons. Once we've read in 180 * the information for an entry, go ahead and try to 181 * add it to the policy vector. 182 * 183 */ 184 185 lookahead = st.nextToken(); 186 while (lookahead != StreamTokenizer.TT_EOF) { 187 if (peek("grant")) { 188 GrantEntry ge = parseGrantEntry(); 189 // could be null if we couldn't expand a property 190 if (ge != null) 191 add(ge); 192 } else if (peek("keystore") && keyStoreUrlString==null) { 193 // only one keystore entry per policy file, others will be 194 // ignored 195 parseKeyStoreEntry(); 196 } else { 197 // error? 198 } 199 match(";"); 200 } 201 } 202 203 public void add(GrantEntry ge) 204 { 205 grantEntries.addElement(ge); 206 } 207 208 public void replace(GrantEntry origGe, GrantEntry newGe) 209 { 210 grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe)); 211 } 212 213 public boolean remove(GrantEntry ge) 214 { 215 return grantEntries.removeElement(ge); 216 } 217 218 /** 219 * Returns the (possibly expanded) keystore location, or null if the 220 * expansion fails. 221 */ 222 public String getKeyStoreUrl() { 223 try { 224 if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) { 225 return expand(keyStoreUrlString).replace(File.separatorChar, 226 '/'); 227 } 228 } catch (PropertyExpander.ExpandException peee) { 229 return null; 230 } 231 return null; 232 } 233 234 public void setKeyStoreUrl(String url) { 235 keyStoreUrlString = url; 236 } 237 238 public String getKeyStoreType() { 239 return keyStoreType; 240 } 241 242 public void setKeyStoreType(String type) { 243 keyStoreType = type; 244 } 245 246 /** 247 * Enumerate all the entries in the global policy object. 248 * This method is used by policy admin tools. The tools 249 * should use the Enumeration methods on the returned object 250 * to fetch the elements sequentially. 251 */ 252 public Enumeration<GrantEntry> grantElements(){ 253 return grantEntries.elements(); 254 } 255 256 /** 257 * write out the policy 258 */ 259 260 public void write(Writer policy) 261 { 262 PrintWriter out = new PrintWriter(new BufferedWriter(policy)); 263 264 Enumeration<GrantEntry> enum_ = grantElements(); 265 266 out.println("/* AUTOMATICALLY GENERATED ON "+ 267 (new java.util.Date()) + "*/"); 268 out.println("/* DO NOT EDIT */"); 269 out.println(); 270 271 // write the (unexpanded) keystore entry as the first entry of the 272 // policy file 273 if (keyStoreUrlString != null) { 274 writeKeyStoreEntry(out); 275 } 276 277 // write "grant" entries 278 while (enum_.hasMoreElements()) { 279 GrantEntry ge = enum_.nextElement(); 280 ge.write(out); 281 out.println(); 282 } 283 out.flush(); 284 } 285 286 /** 287 * parses a keystore entry 288 */ 289 private void parseKeyStoreEntry() throws ParsingException, IOException { 290 match("keystore"); 291 keyStoreUrlString = match("quoted string"); 292 293 // parse keystore type 294 if (!peek(",")) { 295 return; // default type 296 } 297 match(","); 298 299 if (peek("\"")) { 300 keyStoreType = match("quoted string"); 301 } else { 302 throw new ParsingException(st.lineno(), 303 rb.getString("expected keystore type")); 304 } 305 } 306 307 /** 308 * writes the (unexpanded) keystore entry 309 */ 310 private void writeKeyStoreEntry(PrintWriter out) { 311 out.print("keystore \""); 312 out.print(keyStoreUrlString); 313 out.print('"'); 314 if (keyStoreType != null && keyStoreType.length() > 0) 315 out.print(", \"" + keyStoreType + "\""); 316 out.println(";"); 317 out.println(); 318 } 319 320 /** 321 * parse a Grant entry 322 */ 323 private GrantEntry parseGrantEntry() 324 throws ParsingException, IOException 325 { 326 GrantEntry e = new GrantEntry(); 327 LinkedList<PrincipalEntry> principals = null; 328 boolean ignoreEntry = false; 329 330 match("grant"); 331 332 while(!peek("{")) { 333 334 if (peekAndMatch("Codebase")) { 335 e.codeBase = match("quoted string"); 336 peekAndMatch(","); 337 } else if (peekAndMatch("SignedBy")) { 338 e.signedBy = match("quoted string"); 339 peekAndMatch(","); 340 } else if (peekAndMatch("Principal")) { 341 if (principals == null) { 342 principals = new LinkedList<PrincipalEntry>(); 343 } 344 345 // check for principalClass wildcard 346 String principalClass; 347 if (peek("*")) { 348 match("*"); 349 principalClass = PrincipalEntry.WILDCARD_CLASS; 350 } else { 351 principalClass = match("principal type"); 352 } 353 354 // check for principalName wildcard 355 String principalName; 356 if (peek("*")) { 357 match("*"); 358 principalName = PrincipalEntry.WILDCARD_NAME; 359 } else { 360 principalName = match("quoted string"); 361 } 362 363 // disallow WILDCARD_CLASS && actual name 364 if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) && 365 !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { 366 if (debug != null) 367 debug.println("disallowing principal that has " + 368 "WILDCARD class but no WILDCARD name"); 369 throw new ParsingException 370 (st.lineno(), 371 rb.getString("can not specify Principal with a ") + 372 rb.getString("wildcard class without a wildcard name")); 373 } 374 375 try { 376 principalName = expand(principalName); 377 principals.add 378 (new PrincipalEntry(principalClass, principalName)); 379 } catch (PropertyExpander.ExpandException peee) { 380 // ignore the entire policy entry 381 // but continue parsing all the info 382 // so we can get to the next entry 383 if (debug != null) 384 debug.println("principal name expansion failed: " + 385 principalName); 386 ignoreEntry = true; 387 } 388 peekAndMatch(","); 389 } else { 390 throw new 391 ParsingException(st.lineno(), 392 rb.getString("expected codeBase or SignedBy")); 393 } 394 } 395 396 // disallow non principal-based grant entries 397 if (principals == null) { 398 throw new ParsingException 399 (st.lineno(), 400 rb.getString("only Principal-based grant entries permitted")); 401 } 402 403 e.principals = principals; 404 match("{"); 405 406 while(!peek("}")) { 407 if (peek("Permission")) { 408 try { 409 PermissionEntry pe = parsePermissionEntry(); 410 e.add(pe); 411 } catch (PropertyExpander.ExpandException peee) { 412 // ignore. The add never happened 413 skipEntry(); // BugId 4219343 414 } 415 match(";"); 416 } else { 417 throw new 418 ParsingException(st.lineno(), 419 rb.getString("expected permission entry")); 420 } 421 } 422 match("}"); 423 424 try { 425 if (e.codeBase != null) 426 e.codeBase = expand(e.codeBase).replace(File.separatorChar, '/'); 427 e.signedBy = expand(e.signedBy); 428 } catch (PropertyExpander.ExpandException peee) { 429 return null; 430 } 431 432 return (ignoreEntry == true) ? null : e; 433 } 434 435 /** 436 * parse a Permission entry 437 */ 438 private PermissionEntry parsePermissionEntry() 439 throws ParsingException, IOException, PropertyExpander.ExpandException 440 { 441 PermissionEntry e = new PermissionEntry(); 442 443 // Permission 444 match("Permission"); 445 e.permission = match("permission type"); 446 447 if (peek("\"")) { 448 // Permission name 449 e.name = expand(match("quoted string")); 450 } 451 452 if (!peek(",")) { 453 return e; 454 } 455 match(","); 456 457 if (peek("\"")) { 458 e.action = expand(match("quoted string")); 459 if (!peek(",")) { 460 return e; 461 } 462 match(","); 463 } 464 465 if (peekAndMatch("SignedBy")) { 466 e.signedBy = expand(match("quoted string")); 467 } 468 return e; 469 } 470 471 private boolean peekAndMatch(String expect) 472 throws ParsingException, IOException 473 { 474 if (peek(expect)) { 475 match(expect); 476 return true; 477 } else { 478 return false; 479 } 480 } 481 482 private boolean peek(String expect) { 483 boolean found = false; 484 485 switch (lookahead) { 486 487 case StreamTokenizer.TT_WORD: 488 if (expect.equalsIgnoreCase(st.sval)) 489 found = true; 490 break; 491 case ',': 492 if (expect.equalsIgnoreCase(",")) 493 found = true; 494 break; 495 case '{': 496 if (expect.equalsIgnoreCase("{")) 497 found = true; 498 break; 499 case '}': 500 if (expect.equalsIgnoreCase("}")) 501 found = true; 502 break; 503 case '"': 504 if (expect.equalsIgnoreCase("\"")) 505 found = true; 506 break; 507 case '*': 508 if (expect.equalsIgnoreCase("*")) 509 found = true; 510 break; 511 default: 512 513 } 514 return found; 515 } 516 517 private String match(String expect) 518 throws ParsingException, IOException 519 { 520 String value = null; 521 522 switch (lookahead) { 523 case StreamTokenizer.TT_NUMBER: 524 throw new ParsingException(st.lineno(), expect, 525 rb.getString("number ") + 526 String.valueOf(st.nval)); 527 case StreamTokenizer.TT_EOF: 528 throw new ParsingException 529 (rb.getString("expected ") + expect + 530 rb.getString(", read end of file")); 531 case StreamTokenizer.TT_WORD: 532 if (expect.equalsIgnoreCase(st.sval)) { 533 lookahead = st.nextToken(); 534 } else if (expect.equalsIgnoreCase("permission type")) { 535 value = st.sval; 536 lookahead = st.nextToken(); 537 } else if (expect.equalsIgnoreCase("principal type")) { 538 value = st.sval; 539 lookahead = st.nextToken(); 540 } else { 541 throw new ParsingException(st.lineno(), expect, st.sval); 542 } 543 break; 544 case '"': 545 if (expect.equalsIgnoreCase("quoted string")) { 546 value = st.sval; 547 lookahead = st.nextToken(); 548 } else if (expect.equalsIgnoreCase("permission type")) { 549 value = st.sval; 550 lookahead = st.nextToken(); 551 } else if (expect.equalsIgnoreCase("principal type")) { 552 value = st.sval; 553 lookahead = st.nextToken(); 554 } else { 555 throw new ParsingException(st.lineno(), expect, st.sval); 556 } 557 break; 558 case ',': 559 if (expect.equalsIgnoreCase(",")) 560 lookahead = st.nextToken(); 561 else 562 throw new ParsingException(st.lineno(), expect, ","); 563 break; 564 case '{': 565 if (expect.equalsIgnoreCase("{")) 566 lookahead = st.nextToken(); 567 else 568 throw new ParsingException(st.lineno(), expect, "{"); 569 break; 570 case '}': 571 if (expect.equalsIgnoreCase("}")) 572 lookahead = st.nextToken(); 573 else 574 throw new ParsingException(st.lineno(), expect, "}"); 575 break; 576 case ';': 577 if (expect.equalsIgnoreCase(";")) 578 lookahead = st.nextToken(); 579 else 580 throw new ParsingException(st.lineno(), expect, ";"); 581 break; 582 case '*': 583 if (expect.equalsIgnoreCase("*")) 584 lookahead = st.nextToken(); 585 else 586 throw new ParsingException(st.lineno(), expect, "*"); 587 break; 588 default: 589 throw new ParsingException(st.lineno(), expect, 590 new String(new char[] {(char)lookahead})); 591 } 592 return value; 593 } 594 595 /** 596 * skip all tokens for this entry leaving the delimiter ";" 597 * in the stream. 598 */ 599 private void skipEntry() 600 throws ParsingException, IOException 601 { 602 while(lookahead != ';') { 603 switch (lookahead) { 604 case StreamTokenizer.TT_NUMBER: 605 throw new ParsingException(st.lineno(), ";", 606 rb.getString("number ") + 607 String.valueOf(st.nval)); 608 case StreamTokenizer.TT_EOF: 609 throw new ParsingException 610 (rb.getString("expected ';', read end of file")); 611 default: 612 lookahead = st.nextToken(); 613 } 614 } 615 } 616 617 /** 618 * Each grant entry in the policy configuration file is 619 * represented by a 620 * GrantEntry object. <p> 621 * 622 * <p> 623 * For example, the entry 624 * <pre> 625 * grant signedBy "Duke" { 626 * permission java.io.FilePermission "/tmp", "read,write"; 627 * }; 628 * 629 * </pre> 630 * is represented internally 631 * <pre> 632 * 633 * pe = new PermissionEntry("java.io.FilePermission", 634 * "/tmp", "read,write"); 635 * 636 * ge = new GrantEntry("Duke", null); 637 * 638 * ge.add(pe); 639 * 640 * </pre> 641 * 642 * @author Roland Schemers 643 * 644 * version 1.19, 05/21/98 645 */ 646 647 static class GrantEntry { 648 649 public String signedBy; 650 public String codeBase; 651 public LinkedList<PrincipalEntry> principals; 652 public Vector<PermissionEntry> permissionEntries; 653 654 public GrantEntry() { 655 permissionEntries = new Vector<PermissionEntry>(); 656 } 657 658 public GrantEntry(String signedBy, String codeBase) { 659 this.codeBase = codeBase; 660 this.signedBy = signedBy; 661 permissionEntries = new Vector<PermissionEntry>(); 662 } 663 664 public void add(PermissionEntry pe) 665 { 666 permissionEntries.addElement(pe); 667 } 668 669 public boolean remove(PermissionEntry pe) 670 { 671 return permissionEntries.removeElement(pe); 672 } 673 674 public boolean contains(PermissionEntry pe) 675 { 676 return permissionEntries.contains(pe); 677 } 678 679 /** 680 * Enumerate all the permission entries in this GrantEntry. 681 */ 682 public Enumeration<PermissionEntry> permissionElements(){ 683 return permissionEntries.elements(); 684 } 685 686 687 public void write(PrintWriter out) { 688 out.print("grant"); 689 if (signedBy != null) { 690 out.print(" signedBy \""); 691 out.print(signedBy); 692 out.print('"'); 693 if (codeBase != null) 694 out.print(", "); 695 } 696 if (codeBase != null) { 697 out.print(" codeBase \""); 698 out.print(codeBase); 699 out.print('"'); 700 if (principals != null && principals.size() > 0) 701 out.print(",\n"); 702 } 703 if (principals != null && principals.size() > 0) { 704 ListIterator<PrincipalEntry> pli = principals.listIterator(); 705 while (pli.hasNext()) { 706 out.print("\tPrincipal "); 707 PrincipalEntry pe = pli.next(); 708 out.print(pe.principalClass + 709 " \"" + pe.principalName + "\""); 710 if (pli.hasNext()) 711 out.print(",\n"); 712 } 713 } 714 out.println(" {"); 715 Enumeration<PermissionEntry> enum_ = permissionEntries.elements(); 716 while (enum_.hasMoreElements()) { 717 PermissionEntry pe = enum_.nextElement(); 718 out.write(" "); 719 pe.write(out); 720 } 721 out.println("};"); 722 } 723 724 } 725 726 /** 727 * Principal info (class and name) in a grant entry 728 */ 729 static class PrincipalEntry { 730 731 static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS"; 732 static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME"; 733 734 String principalClass; 735 String principalName; 736 737 /** 738 * A PrincipalEntry consists of the <code>Principal</code> 739 * class and <code>Principal</code> name. 740 * 741 * <p> 742 * 743 * @param principalClass the <code>Principal</code> class. <p> 744 * 745 * @param principalName the <code>Principal</code> name. <p> 746 */ 747 public PrincipalEntry(String principalClass, String principalName) { 748 if (principalClass == null || principalName == null) 749 throw new NullPointerException 750 ("null principalClass or principalName"); 751 this.principalClass = principalClass; 752 this.principalName = principalName; 753 } 754 755 /** 756 * Test for equality between the specified object and this object. 757 * Two PrincipalEntries are equal if their PrincipalClass and 758 * PrincipalName values are equal. 759 * 760 * <p> 761 * 762 * @param obj the object to test for equality with this object. 763 * 764 * @return true if the objects are equal, false otherwise. 765 */ 766 public boolean equals(Object obj) { 767 if (this == obj) 768 return true; 769 770 if (!(obj instanceof PrincipalEntry)) 771 return false; 772 773 PrincipalEntry that = (PrincipalEntry)obj; 774 if (this.principalClass.equals(that.principalClass) && 775 this.principalName.equals(that.principalName)) { 776 return true; 777 } 778 779 return false; 780 } 781 782 /** 783 * Return a hashcode for this <code>PrincipalEntry</code>. 784 * 785 * <p> 786 * 787 * @return a hashcode for this <code>PrincipalEntry</code>. 788 */ 789 public int hashCode() { 790 return principalClass.hashCode(); 791 } 792 } 793 794 /** 795 * Each permission entry in the policy configuration file is 796 * represented by a 797 * PermissionEntry object. <p> 798 * 799 * <p> 800 * For example, the entry 801 * <pre> 802 * permission java.io.FilePermission "/tmp", "read,write"; 803 * </pre> 804 * is represented internally 805 * <pre> 806 * 807 * pe = new PermissionEntry("java.io.FilePermission", 808 * "/tmp", "read,write"); 809 * </pre> 810 * 811 * @author Roland Schemers 812 * 813 * version 1.19, 05/21/98 814 */ 815 816 static class PermissionEntry { 817 818 public String permission; 819 public String name; 820 public String action; 821 public String signedBy; 822 823 public PermissionEntry() { 824 } 825 826 public PermissionEntry(String permission, 827 String name, 828 String action) { 829 this.permission = permission; 830 this.name = name; 831 this.action = action; 832 } 833 834 /** 835 * Calculates a hash code value for the object. Objects 836 * which are equal will also have the same hashcode. 837 */ 838 public int hashCode() { 839 int retval = permission.hashCode(); 840 if (name != null) retval ^= name.hashCode(); 841 if (action != null) retval ^= action.hashCode(); 842 return retval; 843 } 844 845 public boolean equals(Object obj) { 846 if (obj == this) 847 return true; 848 849 if (! (obj instanceof PermissionEntry)) 850 return false; 851 852 PermissionEntry that = (PermissionEntry) obj; 853 854 if (this.permission == null) { 855 if (that.permission != null) return false; 856 } else { 857 if (!this.permission.equals(that.permission)) return false; 858 } 859 860 if (this.name == null) { 861 if (that.name != null) return false; 862 } else { 863 if (!this.name.equals(that.name)) return false; 864 } 865 866 if (this.action == null) { 867 if (that.action != null) return false; 868 } else { 869 if (!this.action.equals(that.action)) return false; 870 } 871 872 if (this.signedBy == null) { 873 if (that.signedBy != null) return false; 874 } else { 875 if (!this.signedBy.equals(that.signedBy)) return false; 876 } 877 878 // everything matched -- the 2 objects are equal 879 return true; 880 } 881 882 public void write(PrintWriter out) { 883 out.print("permission "); 884 out.print(permission); 885 if (name != null) { 886 out.print(" \""); 887 888 // have to add escape chars for quotes 889 if (name.indexOf("\"") != -1) { 890 int numQuotes = 0; 891 char[] chars = name.toCharArray(); 892 893 // count the number of quote chars 894 for (int i = 0; i < chars.length; i++) { 895 if (chars[i] == '"') 896 numQuotes++; 897 } 898 899 // now, add an escape char before each quote 900 char[] newChars = new char[chars.length + numQuotes]; 901 for (int i = 0, j = 0; i < chars.length; i++) { 902 if (chars[i] != '"') { 903 newChars[j++] = chars[i]; 904 } else { 905 newChars[j++] = '\\'; 906 newChars[j++] = chars[i]; 907 } 908 } 909 name = new String(newChars); 910 } 911 out.print(name); 912 out.print('"'); 913 } 914 if (action != null) { 915 out.print(", \""); 916 out.print(action); 917 out.print('"'); 918 } 919 if (signedBy != null) { 920 out.print(", signedBy \""); 921 out.print(signedBy); 922 out.print('"'); 923 } 924 out.println(";"); 925 } 926 } 927 928 static class ParsingException extends GeneralSecurityException { 929 930 private static final long serialVersionUID = 8240970523155877400L; 931 932 /** 933 * Constructs a ParsingException with the specified 934 * detail message. A detail message is a String that describes 935 * this particular exception, which may, for example, specify which 936 * algorithm is not available. 937 * 938 * @param msg the detail message. 939 */ 940 public ParsingException(String msg) { 941 super(msg); 942 } 943 944 public ParsingException(int line, String msg) { 945 super(rb.getString("line ") + line + rb.getString(": ") + msg); 946 } 947 948 public ParsingException(int line, String expect, String actual) { 949 super(rb.getString("line ") + line + rb.getString(": expected '") + 950 expect + rb.getString("', found '") + actual + 951 rb.getString("'")); 952 } 953 } 954 955 public static void main(String arg[]) throws Exception { 956 PolicyParser pp = new PolicyParser(true); 957 pp.read(new FileReader(arg[0])); 958 FileWriter fr = new FileWriter(arg[1]); 959 pp.write(fr); 960 fr.close(); 961 } 962 }