/* * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider; import java.io.*; import java.lang.RuntimePermission; import java.net.SocketPermission; import java.net.URL; import java.security.GeneralSecurityException; import java.security.Principal; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.Vector; import java.util.StringTokenizer; import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; import sun.security.util.PropertyExpander; import sun.security.util.ResourcesMgr; /** * The policy for a Java runtime (specifying * which permissions are available for code from various principals) * is represented as a separate * persistent configuration. The configuration may be stored as a * flat ASCII file, as a serialized binary file of * the Policy class, or as a database.

* *

The Java runtime creates one global Policy object, which is used to * represent the static policy configuration file. It is consulted by * a ProtectionDomain when the protection domain initializes its set of * permissions.

* *

The Policy init method parses the policy * configuration file, and then * populates the Policy object. The Policy object is agnostic in that * it is not involved in making policy decisions. It is merely the * Java runtime representation of the persistent policy configuration * file.

* *

When a protection domain needs to initialize its set of * permissions, it executes code such as the following * to ask the global Policy object to populate a * Permissions object with the appropriate permissions: *

 *  policy = Policy.getPolicy();
 *  Permissions perms = policy.getPermissions(protectiondomain)
 * 
* *

The protection domain contains a CodeSource * object, which encapsulates its codebase (URL) and public key attributes. * It also contains the principals associated with the domain. * The Policy object evaluates the global policy in light of who the * principal is and what the code source is and returns an appropriate * Permissions object. * * @author Roland Schemers * @author Ram Marti * * @since 1.2 */ public class PolicyParser { private static final String EXTDIRS_PROPERTY = "java.ext.dirs"; private static final String OLD_EXTDIRS_EXPANSION = "${" + EXTDIRS_PROPERTY + "}"; // package-private: used by PolicyFile for static policy static final String EXTDIRS_EXPANSION = "${{" + EXTDIRS_PROPERTY + "}}"; private Vector grantEntries; // Convenience variables for parsing private static final Debug debug = Debug.getInstance("parser", "\t[Policy Parser]"); private StreamTokenizer st; private int lookahead; private boolean expandProp = false; private String keyStoreUrlString = null; // unexpanded private String keyStoreType = null; private String keyStoreProvider = null; private String storePassURL = null; private String expand(String value) throws PropertyExpander.ExpandException { return expand(value, false); } private String expand(String value, boolean encodeURL) throws PropertyExpander.ExpandException { if (!expandProp) { return value; } else { return PropertyExpander.expand(value, encodeURL); } } /** * Creates a PolicyParser object. */ public PolicyParser() { grantEntries = new Vector(); } public PolicyParser(boolean expandProp) { this(); this.expandProp = expandProp; } /** * Reads a policy configuration into the Policy object using a * Reader object.

* * @param policy the policy Reader object. * * @exception ParsingException if the policy configuration contains * a syntax error. * * @exception IOException if an error occurs while reading the policy * configuration. */ public void read(Reader policy) throws ParsingException, IOException { if (!(policy instanceof BufferedReader)) { policy = new BufferedReader(policy); } /** * Configure the stream tokenizer: * Recognize strings between "..." * Don't convert words to lowercase * Recognize both C-style and C++-style comments * Treat end-of-line as white space, not as a token */ st = new StreamTokenizer(policy); st.resetSyntax(); st.wordChars('a', 'z'); st.wordChars('A', 'Z'); st.wordChars('.', '.'); st.wordChars('0', '9'); st.wordChars('_', '_'); st.wordChars('$', '$'); st.wordChars(128 + 32, 255); st.whitespaceChars(0, ' '); st.commentChar('/'); st.quoteChar('\''); st.quoteChar('"'); st.lowerCaseMode(false); st.ordinaryChar('/'); st.slashSlashComments(true); st.slashStarComments(true); /** * The main parsing loop. The loop is executed once * for each entry in the config file. The entries * are delimited by semicolons. Once we've read in * the information for an entry, go ahead and try to * add it to the policy vector. * */ lookahead = st.nextToken(); while (lookahead != StreamTokenizer.TT_EOF) { if (peek("grant")) { GrantEntry ge = parseGrantEntry(); // could be null if we couldn't expand a property if (ge != null) add(ge); } else if (peek("keystore") && keyStoreUrlString==null) { // only one keystore entry per policy file, others will be // ignored parseKeyStoreEntry(); } else if (peek("keystorePasswordURL") && storePassURL==null) { // only one keystore passwordURL per policy file, others will be // ignored parseStorePassURL(); } else { // error? } match(";"); } if (keyStoreUrlString == null && storePassURL != null) { throw new ParsingException(ResourcesMgr.getString ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore")); } } public void add(GrantEntry ge) { grantEntries.addElement(ge); } public void replace(GrantEntry origGe, GrantEntry newGe) { grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe)); } public boolean remove(GrantEntry ge) { return grantEntries.removeElement(ge); } /** * Returns the (possibly expanded) keystore location, or null if the * expansion fails. */ public String getKeyStoreUrl() { try { if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) { return expand(keyStoreUrlString, true).replace (File.separatorChar, '/'); } } catch (PropertyExpander.ExpandException peee) { if (debug != null) { debug.println(peee.toString()); } return null; } return null; } public void setKeyStoreUrl(String url) { keyStoreUrlString = url; } public String getKeyStoreType() { return keyStoreType; } public void setKeyStoreType(String type) { keyStoreType = type; } public String getKeyStoreProvider() { return keyStoreProvider; } public void setKeyStoreProvider(String provider) { keyStoreProvider = provider; } public String getStorePassURL() { try { if (storePassURL!=null && storePassURL.length()!=0) { return expand(storePassURL, true).replace (File.separatorChar, '/'); } } catch (PropertyExpander.ExpandException peee) { if (debug != null) { debug.println(peee.toString()); } return null; } return null; } public void setStorePassURL(String storePassURL) { this.storePassURL = storePassURL; } /** * Enumerate all the entries in the global policy object. * This method is used by policy admin tools. The tools * should use the Enumeration methods on the returned object * to fetch the elements sequentially. */ public Enumeration grantElements(){ return grantEntries.elements(); } /** * write out the policy */ public void write(Writer policy) { PrintWriter out = new PrintWriter(new BufferedWriter(policy)); Enumeration enum_ = grantElements(); out.println("/* AUTOMATICALLY GENERATED ON "+ (new java.util.Date()) + "*/"); out.println("/* DO NOT EDIT */"); out.println(); // write the (unexpanded) keystore entry as the first entry of the // policy file if (keyStoreUrlString != null) { writeKeyStoreEntry(out); } if (storePassURL != null) { writeStorePassURL(out); } // write "grant" entries while (enum_.hasMoreElements()) { GrantEntry ge = enum_.nextElement(); ge.write(out); out.println(); } out.flush(); } /** * parses a keystore entry */ private void parseKeyStoreEntry() throws ParsingException, IOException { match("keystore"); keyStoreUrlString = match("quoted string"); // parse keystore type if (!peek(",")) { return; // default type } match(","); if (peek("\"")) { keyStoreType = match("quoted string"); } else { throw new ParsingException(st.lineno(), ResourcesMgr.getString("expected.keystore.type")); } // parse keystore provider if (!peek(",")) { return; // provider optional } match(","); if (peek("\"")) { keyStoreProvider = match("quoted string"); } else { throw new ParsingException(st.lineno(), ResourcesMgr.getString("expected.keystore.provider")); } } private void parseStorePassURL() throws ParsingException, IOException { match("keyStorePasswordURL"); storePassURL = match("quoted string"); } /** * writes the (unexpanded) keystore entry */ private void writeKeyStoreEntry(PrintWriter out) { out.print("keystore \""); out.print(keyStoreUrlString); out.print('"'); if (keyStoreType != null && keyStoreType.length() > 0) out.print(", \"" + keyStoreType + "\""); if (keyStoreProvider != null && keyStoreProvider.length() > 0) out.print(", \"" + keyStoreProvider + "\""); out.println(";"); out.println(); } private void writeStorePassURL(PrintWriter out) { out.print("keystorePasswordURL \""); out.print(storePassURL); out.print('"'); out.println(";"); out.println(); } /** * parse a Grant entry */ private GrantEntry parseGrantEntry() throws ParsingException, IOException { GrantEntry e = new GrantEntry(); LinkedList principals = null; boolean ignoreEntry = false; match("grant"); while(!peek("{")) { if (peekAndMatch("Codebase")) { if (e.codeBase != null) throw new ParsingException( st.lineno(), ResourcesMgr.getString ("multiple.Codebase.expressions")); e.codeBase = match("quoted string"); peekAndMatch(","); } else if (peekAndMatch("SignedBy")) { if (e.signedBy != null) throw new ParsingException( st.lineno(), ResourcesMgr.getString( "multiple.SignedBy.expressions")); e.signedBy = match("quoted string"); // verify syntax of the aliases StringTokenizer aliases = new StringTokenizer(e.signedBy, ",", true); int actr = 0; int cctr = 0; while (aliases.hasMoreTokens()) { String alias = aliases.nextToken().trim(); if (alias.equals(",")) cctr++; else if (alias.length() > 0) actr++; } if (actr <= cctr) throw new ParsingException( st.lineno(), ResourcesMgr.getString( "SignedBy.has.empty.alias")); peekAndMatch(","); } else if (peekAndMatch("Principal")) { if (principals == null) { principals = new LinkedList<>(); } String principalClass; String principalName; if (peek("\"")) { // both the principalClass and principalName // will be replaced later principalClass = PrincipalEntry.REPLACE_NAME; principalName = match("principal type"); } else { // check for principalClass wildcard if (peek("*")) { match("*"); principalClass = PrincipalEntry.WILDCARD_CLASS; } else { principalClass = match("principal type"); } // check for principalName wildcard if (peek("*")) { match("*"); principalName = PrincipalEntry.WILDCARD_NAME; } else { principalName = match("quoted string"); } // disallow WILDCARD_CLASS && actual name if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) && !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { if (debug != null) { debug.println("disallowing principal that " + "has WILDCARD class but no WILDCARD name"); } throw new ParsingException (st.lineno(), ResourcesMgr.getString ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name")); } } try { principalName = expand(principalName); if (principalClass.equals ("javax.security.auth.x500.X500Principal") && !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { // 4702543: X500 names with an EmailAddress // were encoded incorrectly. construct a new // X500Principal with correct encoding. X500Principal p = new X500Principal ((new X500Principal(principalName)).toString()); principalName = p.getName(); } principals.add (new PrincipalEntry(principalClass, principalName)); } catch (PropertyExpander.ExpandException peee) { // ignore the entire policy entry // but continue parsing all the info // so we can get to the next entry if (debug != null) { debug.println("principal name expansion failed: " + principalName); } ignoreEntry = true; } peekAndMatch(","); } else { throw new ParsingException(st.lineno(), ResourcesMgr.getString( "expected.codeBase.or.SignedBy.or.Principal")); } } if (principals != null) e.principals = principals; match("{"); while(!peek("}")) { if (peek("Permission")) { try { PermissionEntry pe = parsePermissionEntry(); e.add(pe); } catch (PropertyExpander.ExpandException peee) { // ignore. The add never happened if (debug != null) { debug.println(peee.toString()); } skipEntry(); // BugId 4219343 } match(";"); } else { throw new ParsingException(st.lineno(), ResourcesMgr.getString( "expected.permission.entry")); } } match("}"); try { if (e.signedBy != null) e.signedBy = expand(e.signedBy); if (e.codeBase != null) { // For backward compatibility with 1.4 if (e.codeBase.equals(OLD_EXTDIRS_EXPANSION)) { e.codeBase = EXTDIRS_EXPANSION; } int es; if ((es=e.codeBase.indexOf(EXTDIRS_EXPANSION)) < 0) { e.codeBase = expand(e.codeBase, true).replace (File.separatorChar, '/'); } else { // expand the system property "java.ext.dirs", // parse it into its path components, // and then create a grant entry for each component String[] extDirs = parseExtDirs(e.codeBase, es); if (extDirs != null && extDirs.length > 0) { for (int i = 0; i < extDirs.length; i++) { GrantEntry newGe = (GrantEntry)e.clone(); newGe.codeBase = extDirs[i]; add(newGe); if (debug != null) { debug.println("creating policy entry for " + "expanded java.ext.dirs path:\n\t\t" + extDirs[i]); } } } ignoreEntry = true; } } } catch (PropertyExpander.ExpandException peee) { if (debug != null) { debug.println(peee.toString()); } return null; } return (ignoreEntry == true) ? null : e; } /** * parse a Permission entry */ private PermissionEntry parsePermissionEntry() throws ParsingException, IOException, PropertyExpander.ExpandException { PermissionEntry e = new PermissionEntry(); // Permission match("Permission"); e.permission = match("permission type"); if (peek("\"")) { // Permission name e.name = expand(match("quoted string")); } if (!peek(",")) { return e; } match(","); if (peek("\"")) { e.action = expand(match("quoted string")); if (!peek(",")) { return e; } match(","); } if (peekAndMatch("SignedBy")) { e.signedBy = expand(match("quoted string")); } return e; } // package-private: used by PolicyFile for static policy static String[] parseExtDirs(String codebase, int start) { String s = System.getProperty(EXTDIRS_PROPERTY); String globalPrefix = (start > 0 ? codebase.substring(0, start) : "file:"); int end = start + EXTDIRS_EXPANSION.length(); String globalSuffix = (end < codebase.length() ? codebase.substring(end) : (String) null); String[] dirs = null; String localSuffix; if (s != null) { StringTokenizer st = new StringTokenizer(s, File.pathSeparator); int count = st.countTokens(); dirs = new String[count]; for (int i = 0; i < count; i++) { File file = new File(st.nextToken()); dirs[i] = sun.net.www.ParseUtil.encodePath (file.getAbsolutePath()); if (!dirs[i].startsWith("/")) { dirs[i] = "/" + dirs[i]; } localSuffix = (globalSuffix == null ? (dirs[i].endsWith("/") ? "*" : "/*") : globalSuffix); dirs[i] = globalPrefix + dirs[i] + localSuffix; } } return dirs; } private boolean peekAndMatch(String expect) throws ParsingException, IOException { if (peek(expect)) { match(expect); return true; } else { return false; } } private boolean peek(String expect) { boolean found = false; switch (lookahead) { case StreamTokenizer.TT_WORD: if (expect.equalsIgnoreCase(st.sval)) found = true; break; case ',': if (expect.equalsIgnoreCase(",")) found = true; break; case '{': if (expect.equalsIgnoreCase("{")) found = true; break; case '}': if (expect.equalsIgnoreCase("}")) found = true; break; case '"': if (expect.equalsIgnoreCase("\"")) found = true; break; case '*': if (expect.equalsIgnoreCase("*")) found = true; break; default: } return found; } private String match(String expect) throws ParsingException, IOException { String value = null; switch (lookahead) { case StreamTokenizer.TT_NUMBER: throw new ParsingException(st.lineno(), expect, ResourcesMgr.getString("number.") + String.valueOf(st.nval)); case StreamTokenizer.TT_EOF: MessageFormat form = new MessageFormat( ResourcesMgr.getString ("expected.expect.read.end.of.file.")); Object[] source = {expect}; throw new ParsingException(form.format(source)); case StreamTokenizer.TT_WORD: if (expect.equalsIgnoreCase(st.sval)) { lookahead = st.nextToken(); } else if (expect.equalsIgnoreCase("permission type")) { value = st.sval; lookahead = st.nextToken(); } else if (expect.equalsIgnoreCase("principal type")) { value = st.sval; lookahead = st.nextToken(); } else { throw new ParsingException(st.lineno(), expect, st.sval); } break; case '"': if (expect.equalsIgnoreCase("quoted string")) { value = st.sval; lookahead = st.nextToken(); } else if (expect.equalsIgnoreCase("permission type")) { value = st.sval; lookahead = st.nextToken(); } else if (expect.equalsIgnoreCase("principal type")) { value = st.sval; lookahead = st.nextToken(); } else { throw new ParsingException(st.lineno(), expect, st.sval); } break; case ',': if (expect.equalsIgnoreCase(",")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, ","); break; case '{': if (expect.equalsIgnoreCase("{")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, "{"); break; case '}': if (expect.equalsIgnoreCase("}")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, "}"); break; case ';': if (expect.equalsIgnoreCase(";")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, ";"); break; case '*': if (expect.equalsIgnoreCase("*")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, "*"); break; default: throw new ParsingException(st.lineno(), expect, new String(new char[] {(char)lookahead})); } return value; } /** * skip all tokens for this entry leaving the delimiter ";" * in the stream. */ private void skipEntry() throws ParsingException, IOException { while(lookahead != ';') { switch (lookahead) { case StreamTokenizer.TT_NUMBER: throw new ParsingException(st.lineno(), ";", ResourcesMgr.getString("number.") + String.valueOf(st.nval)); case StreamTokenizer.TT_EOF: throw new ParsingException(ResourcesMgr.getString ("expected.read.end.of.file.")); default: lookahead = st.nextToken(); } } } /** * Each grant entry in the policy configuration file is * represented by a * GrantEntry object.

* *

* For example, the entry *

     *      grant signedBy "Duke" {
     *          permission java.io.FilePermission "/tmp", "read,write";
     *      };
     *
     * 
* is represented internally *
     *
     * pe = new PermissionEntry("java.io.FilePermission",
     *                           "/tmp", "read,write");
     *
     * ge = new GrantEntry("Duke", null);
     *
     * ge.add(pe);
     *
     * 
* * @author Roland Schemers * * version 1.19, 05/21/98 */ public static class GrantEntry { public String signedBy; public String codeBase; public LinkedList principals; public Vector permissionEntries; public GrantEntry() { principals = new LinkedList(); permissionEntries = new Vector(); } public GrantEntry(String signedBy, String codeBase) { this.codeBase = codeBase; this.signedBy = signedBy; principals = new LinkedList(); permissionEntries = new Vector(); } public void add(PermissionEntry pe) { permissionEntries.addElement(pe); } public boolean remove(PrincipalEntry pe) { return principals.remove(pe); } public boolean remove(PermissionEntry pe) { return permissionEntries.removeElement(pe); } public boolean contains(PrincipalEntry pe) { return principals.contains(pe); } public boolean contains(PermissionEntry pe) { return permissionEntries.contains(pe); } /** * Enumerate all the permission entries in this GrantEntry. */ public Enumeration permissionElements(){ return permissionEntries.elements(); } public void write(PrintWriter out) { out.print("grant"); if (signedBy != null) { out.print(" signedBy \""); out.print(signedBy); out.print('"'); if (codeBase != null) out.print(", "); } if (codeBase != null) { out.print(" codeBase \""); out.print(codeBase); out.print('"'); if (principals != null && principals.size() > 0) out.print(",\n"); } if (principals != null && principals.size() > 0) { Iterator pli = principals.iterator(); while (pli.hasNext()) { out.print(" "); PrincipalEntry pe = pli.next(); pe.write(out); if (pli.hasNext()) out.print(",\n"); } } out.println(" {"); Enumeration enum_ = permissionEntries.elements(); while (enum_.hasMoreElements()) { PermissionEntry pe = enum_.nextElement(); out.write(" "); pe.write(out); } out.println("};"); } public Object clone() { GrantEntry ge = new GrantEntry(); ge.codeBase = this.codeBase; ge.signedBy = this.signedBy; ge.principals = new LinkedList(this.principals); ge.permissionEntries = new Vector(this.permissionEntries); return ge; } } /** * Principal info (class and name) in a grant entry */ public static class PrincipalEntry implements Principal { public static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS"; public static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME"; public static final String REPLACE_NAME = "PolicyParser.REPLACE_NAME"; String principalClass; String principalName; /** * A PrincipalEntry consists of the Principal class and Principal name. * * @param principalClass the Principal class * @param principalName the Principal name * @throws NullPointerException if principalClass or principalName * are null */ public PrincipalEntry(String principalClass, String principalName) { if (principalClass == null || principalName == null) throw new NullPointerException(ResourcesMgr.getString( "null.principalClass.or.principalName")); this.principalClass = principalClass; this.principalName = principalName; } boolean isWildcardName() { return principalName.equals(WILDCARD_NAME); } boolean isWildcardClass() { return principalClass.equals(WILDCARD_CLASS); } boolean isReplaceName() { return principalClass.equals(REPLACE_NAME); } public String getPrincipalClass() { return principalClass; } public String getPrincipalName() { return principalName; } public String getDisplayClass() { if (isWildcardClass()) { return "*"; } else if (isReplaceName()) { return ""; } else return principalClass; } public String getDisplayName() { return getDisplayName(false); } public String getDisplayName(boolean addQuote) { if (isWildcardName()) { return "*"; } else { if (addQuote) return "\"" + principalName + "\""; else return principalName; } } @Override public String getName() { return principalName; } @Override public String toString() { if (!isReplaceName()) { return getDisplayClass() + "/" + getDisplayName(); } else { return getDisplayName(); } } /** * Test for equality between the specified object and this object. * Two PrincipalEntries are equal if their class and name values * are equal. * * @param obj the object to test for equality with this object * @return true if the objects are equal, false otherwise */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof PrincipalEntry)) return false; PrincipalEntry that = (PrincipalEntry)obj; return (principalClass.equals(that.principalClass) && principalName.equals(that.principalName)); } /** * Return a hashcode for this PrincipalEntry. * * @return a hashcode for this PrincipalEntry */ @Override public int hashCode() { return principalClass.hashCode(); } public void write(PrintWriter out) { out.print("principal " + getDisplayClass() + " " + getDisplayName(true)); } } /** * Each permission entry in the policy configuration file is * represented by a * PermissionEntry object.

* *

* For example, the entry *

     *          permission java.io.FilePermission "/tmp", "read,write";
     * 
* is represented internally *
     *
     * pe = new PermissionEntry("java.io.FilePermission",
     *                           "/tmp", "read,write");
     * 
* * @author Roland Schemers * * version 1.19, 05/21/98 */ public static class PermissionEntry { public String permission; public String name; public String action; public String signedBy; public PermissionEntry() { } public PermissionEntry(String permission, String name, String action) { this.permission = permission; this.name = name; this.action = action; } /** * Calculates a hash code value for the object. Objects * which are equal will also have the same hashcode. */ @Override public int hashCode() { int retval = permission.hashCode(); if (name != null) retval ^= name.hashCode(); if (action != null) retval ^= action.hashCode(); return retval; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (! (obj instanceof PermissionEntry)) return false; PermissionEntry that = (PermissionEntry) obj; if (this.permission == null) { if (that.permission != null) return false; } else { if (!this.permission.equals(that.permission)) return false; } if (this.name == null) { if (that.name != null) return false; } else { if (!this.name.equals(that.name)) return false; } if (this.action == null) { if (that.action != null) return false; } else { if (!this.action.equals(that.action)) return false; } if (this.signedBy == null) { if (that.signedBy != null) return false; } else { if (!this.signedBy.equals(that.signedBy)) return false; } // everything matched -- the 2 objects are equal return true; } public void write(PrintWriter out) { out.print("permission "); out.print(permission); if (name != null) { out.print(" \""); // ATTENTION: regex with double escaping, // the normal forms look like: // $name =~ s/\\/\\\\/g; and // $name =~ s/\"/\\\"/g; // and then in a java string, it's escaped again out.print(name.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\\\"")); out.print('"'); } if (action != null) { out.print(", \""); out.print(action); out.print('"'); } if (signedBy != null) { out.print(", signedBy \""); out.print(signedBy); out.print('"'); } out.println(";"); } } public static class ParsingException extends GeneralSecurityException { private static final long serialVersionUID = -4330692689482574072L; private String i18nMessage; /** * Constructs a ParsingException with the specified * detail message. A detail message is a String that describes * this particular exception, which may, for example, specify which * algorithm is not available. * * @param msg the detail message. */ public ParsingException(String msg) { super(msg); i18nMessage = msg; } public ParsingException(int line, String msg) { super("line " + line + ": " + msg); MessageFormat form = new MessageFormat (ResourcesMgr.getString("line.number.msg")); Object[] source = {new Integer(line), msg}; i18nMessage = form.format(source); } public ParsingException(int line, String expect, String actual) { super("line " + line + ": expected [" + expect + "], found [" + actual + "]"); MessageFormat form = new MessageFormat(ResourcesMgr.getString ("line.number.expected.expect.found.actual.")); Object[] source = {new Integer(line), expect, actual}; i18nMessage = form.format(source); } @Override public String getLocalizedMessage() { return i18nMessage; } } public static void main(String arg[]) throws Exception { try (FileReader fr = new FileReader(arg[0]); FileWriter fw = new FileWriter(arg[1])) { PolicyParser pp = new PolicyParser(true); pp.read(fr); pp.write(fw); } } }