src/java.base/share/classes/sun/security/provider/PolicyParser.java
changeset 47216 71c04702a3d5
parent 43297 05ad35b943d0
child 48757 8cc67294ec56
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2017, 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 sun.security.provider;
       
    27 
       
    28 import java.io.*;
       
    29 import java.security.GeneralSecurityException;
       
    30 import java.security.Principal;
       
    31 import java.util.*;
       
    32 import javax.security.auth.x500.X500Principal;
       
    33 
       
    34 import sun.security.util.Debug;
       
    35 import sun.security.util.PropertyExpander;
       
    36 import sun.security.util.LocalizedMessage;
       
    37 
       
    38 /**
       
    39  * The policy for a Java runtime (specifying
       
    40  * which permissions are available for code from various principals)
       
    41  * is represented as a separate
       
    42  * persistent configuration.  The configuration may be stored as a
       
    43  * flat ASCII file, as a serialized binary file of
       
    44  * the Policy class, or as a database.
       
    45  *
       
    46  * <p>The Java runtime creates one global Policy object, which is used to
       
    47  * represent the static policy configuration file.  It is consulted by
       
    48  * a ProtectionDomain when the protection domain initializes its set of
       
    49  * permissions.
       
    50  *
       
    51  * <p>The Policy <code>init</code> method parses the policy
       
    52  * configuration file, and then
       
    53  * populates the Policy object.  The Policy object is agnostic in that
       
    54  * it is not involved in making policy decisions.  It is merely the
       
    55  * Java runtime representation of the persistent policy configuration
       
    56  * file.
       
    57  *
       
    58  * <p>When a protection domain needs to initialize its set of
       
    59  * permissions, it executes code such as the following
       
    60  * to ask the global Policy object to populate a
       
    61  * Permissions object with the appropriate permissions:
       
    62  * <pre>
       
    63  *  policy = Policy.getPolicy();
       
    64  *  Permissions perms = policy.getPermissions(protectiondomain)
       
    65  * </pre>
       
    66  *
       
    67  * <p>The protection domain contains a CodeSource
       
    68  * object, which encapsulates its codebase (URL) and public key attributes.
       
    69  * It also contains the principals associated with the domain.
       
    70  * The Policy object evaluates the global policy in light of who the
       
    71  * principal is and what the code source is and returns an appropriate
       
    72  * Permissions object.
       
    73  *
       
    74  * @author Roland Schemers
       
    75  * @author Ram Marti
       
    76  *
       
    77  * @since 1.2
       
    78  */
       
    79 
       
    80 public class PolicyParser {
       
    81 
       
    82     private Vector<GrantEntry> grantEntries;
       
    83     private Map<String, DomainEntry> domainEntries;
       
    84 
       
    85     // Convenience variables for parsing
       
    86     private static final Debug debug = Debug.getInstance("parser",
       
    87                                                 "\t[Policy Parser]");
       
    88     private StreamTokenizer st;
       
    89     private int lookahead;
       
    90     private boolean expandProp = false;
       
    91     private String keyStoreUrlString = null; // unexpanded
       
    92     private String keyStoreType = null;
       
    93     private String keyStoreProvider = null;
       
    94     private String storePassURL = null;
       
    95 
       
    96     private String expand(String value)
       
    97         throws PropertyExpander.ExpandException
       
    98     {
       
    99         return expand(value, false);
       
   100     }
       
   101 
       
   102     private String expand(String value, boolean encodeURL)
       
   103         throws PropertyExpander.ExpandException
       
   104     {
       
   105         if (!expandProp) {
       
   106             return value;
       
   107         } else {
       
   108             return PropertyExpander.expand(value, encodeURL);
       
   109         }
       
   110     }
       
   111 
       
   112     /**
       
   113      * Creates a PolicyParser object.
       
   114      */
       
   115 
       
   116     public PolicyParser() {
       
   117         grantEntries = new Vector<GrantEntry>();
       
   118     }
       
   119 
       
   120 
       
   121     public PolicyParser(boolean expandProp) {
       
   122         this();
       
   123         this.expandProp = expandProp;
       
   124     }
       
   125 
       
   126     /**
       
   127      * Reads a policy configuration into the Policy object using a
       
   128      * Reader object. <p>
       
   129      *
       
   130      * @param policy the policy Reader object.
       
   131      *
       
   132      * @exception ParsingException if the policy configuration contains
       
   133      *          a syntax error.
       
   134      *
       
   135      * @exception IOException if an error occurs while reading the policy
       
   136      *          configuration.
       
   137      */
       
   138 
       
   139     public void read(Reader policy)
       
   140         throws ParsingException, IOException
       
   141     {
       
   142         if (!(policy instanceof BufferedReader)) {
       
   143             policy = new BufferedReader(policy);
       
   144         }
       
   145 
       
   146         /**
       
   147          * Configure the stream tokenizer:
       
   148          *      Recognize strings between "..."
       
   149          *      Don't convert words to lowercase
       
   150          *      Recognize both C-style and C++-style comments
       
   151          *      Treat end-of-line as white space, not as a token
       
   152          */
       
   153         st   = new StreamTokenizer(policy);
       
   154 
       
   155         st.resetSyntax();
       
   156         st.wordChars('a', 'z');
       
   157         st.wordChars('A', 'Z');
       
   158         st.wordChars('.', '.');
       
   159         st.wordChars('0', '9');
       
   160         st.wordChars('_', '_');
       
   161         st.wordChars('$', '$');
       
   162         st.wordChars(128 + 32, 255);
       
   163         st.whitespaceChars(0, ' ');
       
   164         st.commentChar('/');
       
   165         st.quoteChar('\'');
       
   166         st.quoteChar('"');
       
   167         st.lowerCaseMode(false);
       
   168         st.ordinaryChar('/');
       
   169         st.slashSlashComments(true);
       
   170         st.slashStarComments(true);
       
   171 
       
   172         /**
       
   173          * The main parsing loop.  The loop is executed once
       
   174          * for each entry in the config file.      The entries
       
   175          * are delimited by semicolons.   Once we've read in
       
   176          * the information for an entry, go ahead and try to
       
   177          * add it to the policy vector.
       
   178          *
       
   179          */
       
   180 
       
   181         lookahead = st.nextToken();
       
   182         GrantEntry ge = null;
       
   183         while (lookahead != StreamTokenizer.TT_EOF) {
       
   184             if (peek("grant")) {
       
   185                 ge = parseGrantEntry();
       
   186                 // could be null if we couldn't expand a property
       
   187                 if (ge != null)
       
   188                     add(ge);
       
   189             } else if (peek("keystore") && keyStoreUrlString==null) {
       
   190                 // only one keystore entry per policy file, others will be
       
   191                 // ignored
       
   192                 parseKeyStoreEntry();
       
   193             } else if (peek("keystorePasswordURL") && storePassURL==null) {
       
   194                 // only one keystore passwordURL per policy file, others will be
       
   195                 // ignored
       
   196                 parseStorePassURL();
       
   197             } else if (ge == null && keyStoreUrlString == null &&
       
   198                 storePassURL == null && peek("domain")) {
       
   199                 if (domainEntries == null) {
       
   200                     domainEntries = new TreeMap<>();
       
   201                 }
       
   202                 DomainEntry de = parseDomainEntry();
       
   203                 if (de != null) {
       
   204                     String domainName = de.getName();
       
   205                     if (!domainEntries.containsKey(domainName)) {
       
   206                         domainEntries.put(domainName, de);
       
   207                     } else {
       
   208                         LocalizedMessage localizedMsg =
       
   209                             new LocalizedMessage("duplicate.keystore.domain.name");
       
   210                         Object[] source = {domainName};
       
   211                         String msg = "duplicate keystore domain name: " +
       
   212                                      domainName;
       
   213                         throw new ParsingException(msg, localizedMsg, source);
       
   214                     }
       
   215                 }
       
   216             } else {
       
   217                 // error?
       
   218             }
       
   219             match(";");
       
   220         }
       
   221 
       
   222         if (keyStoreUrlString == null && storePassURL != null) {
       
   223             throw new ParsingException(LocalizedMessage.getMessage
       
   224                 ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
       
   225         }
       
   226     }
       
   227 
       
   228     public void add(GrantEntry ge)
       
   229     {
       
   230         grantEntries.addElement(ge);
       
   231     }
       
   232 
       
   233     public void replace(GrantEntry origGe, GrantEntry newGe)
       
   234     {
       
   235         grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe));
       
   236     }
       
   237 
       
   238     public boolean remove(GrantEntry ge)
       
   239     {
       
   240         return grantEntries.removeElement(ge);
       
   241     }
       
   242 
       
   243     /**
       
   244      * Returns the (possibly expanded) keystore location, or null if the
       
   245      * expansion fails.
       
   246      */
       
   247     public String getKeyStoreUrl() {
       
   248         try {
       
   249             if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) {
       
   250                 return expand(keyStoreUrlString, true).replace
       
   251                                                 (File.separatorChar, '/');
       
   252             }
       
   253         } catch (PropertyExpander.ExpandException peee) {
       
   254             if (debug != null) {
       
   255                 debug.println(peee.toString());
       
   256             }
       
   257             return null;
       
   258         }
       
   259         return null;
       
   260     }
       
   261 
       
   262     public void setKeyStoreUrl(String url) {
       
   263         keyStoreUrlString = url;
       
   264     }
       
   265 
       
   266     public String getKeyStoreType() {
       
   267         return keyStoreType;
       
   268     }
       
   269 
       
   270     public void setKeyStoreType(String type) {
       
   271         keyStoreType = type;
       
   272     }
       
   273 
       
   274     public String getKeyStoreProvider() {
       
   275         return keyStoreProvider;
       
   276     }
       
   277 
       
   278     public void setKeyStoreProvider(String provider) {
       
   279         keyStoreProvider = provider;
       
   280     }
       
   281 
       
   282     public String getStorePassURL() {
       
   283         try {
       
   284             if (storePassURL!=null && storePassURL.length()!=0) {
       
   285                 return expand(storePassURL, true).replace
       
   286                                                 (File.separatorChar, '/');
       
   287             }
       
   288         } catch (PropertyExpander.ExpandException peee) {
       
   289             if (debug != null) {
       
   290                 debug.println(peee.toString());
       
   291             }
       
   292             return null;
       
   293         }
       
   294         return null;
       
   295     }
       
   296 
       
   297     public void setStorePassURL(String storePassURL) {
       
   298         this.storePassURL = storePassURL;
       
   299     }
       
   300 
       
   301     /**
       
   302      * Enumerate all the entries in the global policy object.
       
   303      * This method is used by policy admin tools.   The tools
       
   304      * should use the Enumeration methods on the returned object
       
   305      * to fetch the elements sequentially.
       
   306      */
       
   307     public Enumeration<GrantEntry> grantElements(){
       
   308         return grantEntries.elements();
       
   309     }
       
   310 
       
   311     public Collection<DomainEntry> getDomainEntries() {
       
   312         return domainEntries.values();
       
   313     }
       
   314 
       
   315     /**
       
   316      * write out the policy
       
   317      */
       
   318 
       
   319     public void write(Writer policy)
       
   320     {
       
   321         PrintWriter out = new PrintWriter(new BufferedWriter(policy));
       
   322 
       
   323         Enumeration<GrantEntry> enum_ = grantElements();
       
   324 
       
   325         out.println("/* AUTOMATICALLY GENERATED ON "+
       
   326                     (new java.util.Date()) + "*/");
       
   327         out.println("/* DO NOT EDIT */");
       
   328         out.println();
       
   329 
       
   330         // write the (unexpanded) keystore entry as the first entry of the
       
   331         // policy file
       
   332         if (keyStoreUrlString != null) {
       
   333             writeKeyStoreEntry(out);
       
   334         }
       
   335         if (storePassURL != null) {
       
   336             writeStorePassURL(out);
       
   337         }
       
   338 
       
   339         // write "grant" entries
       
   340         while (enum_.hasMoreElements()) {
       
   341             GrantEntry ge = enum_.nextElement();
       
   342             ge.write(out);
       
   343             out.println();
       
   344         }
       
   345         out.flush();
       
   346     }
       
   347 
       
   348     /**
       
   349      * parses a keystore entry
       
   350      */
       
   351     private void parseKeyStoreEntry() throws ParsingException, IOException {
       
   352         match("keystore");
       
   353         keyStoreUrlString = match("quoted string");
       
   354 
       
   355         // parse keystore type
       
   356         if (!peek(",")) {
       
   357             return; // default type
       
   358         }
       
   359         match(",");
       
   360 
       
   361         if (peek("\"")) {
       
   362             keyStoreType = match("quoted string");
       
   363         } else {
       
   364             throw new ParsingException(st.lineno(),
       
   365                 LocalizedMessage.getMessage("expected.keystore.type"));
       
   366         }
       
   367 
       
   368         // parse keystore provider
       
   369         if (!peek(",")) {
       
   370             return; // provider optional
       
   371         }
       
   372         match(",");
       
   373 
       
   374         if (peek("\"")) {
       
   375             keyStoreProvider = match("quoted string");
       
   376         } else {
       
   377             throw new ParsingException(st.lineno(),
       
   378                 LocalizedMessage.getMessage("expected.keystore.provider"));
       
   379         }
       
   380     }
       
   381 
       
   382     private void parseStorePassURL() throws ParsingException, IOException {
       
   383         match("keyStorePasswordURL");
       
   384         storePassURL = match("quoted string");
       
   385     }
       
   386 
       
   387     /**
       
   388      * writes the (unexpanded) keystore entry
       
   389      */
       
   390     private void writeKeyStoreEntry(PrintWriter out) {
       
   391         out.print("keystore \"");
       
   392         out.print(keyStoreUrlString);
       
   393         out.print('"');
       
   394         if (keyStoreType != null && keyStoreType.length() > 0)
       
   395             out.print(", \"" + keyStoreType + "\"");
       
   396         if (keyStoreProvider != null && keyStoreProvider.length() > 0)
       
   397             out.print(", \"" + keyStoreProvider + "\"");
       
   398         out.println(";");
       
   399         out.println();
       
   400     }
       
   401 
       
   402     private void writeStorePassURL(PrintWriter out) {
       
   403         out.print("keystorePasswordURL \"");
       
   404         out.print(storePassURL);
       
   405         out.print('"');
       
   406         out.println(";");
       
   407         out.println();
       
   408     }
       
   409 
       
   410     /**
       
   411      * parse a Grant entry
       
   412      */
       
   413     private GrantEntry parseGrantEntry()
       
   414         throws ParsingException, IOException
       
   415     {
       
   416         GrantEntry e = new GrantEntry();
       
   417         LinkedList<PrincipalEntry> principals = null;
       
   418         boolean ignoreEntry = false;
       
   419 
       
   420         match("grant");
       
   421 
       
   422         while(!peek("{")) {
       
   423 
       
   424             if (peekAndMatch("Codebase")) {
       
   425                 if (e.codeBase != null)
       
   426                     throw new ParsingException(
       
   427                             st.lineno(),
       
   428                             LocalizedMessage.getMessage
       
   429                                 ("multiple.Codebase.expressions"));
       
   430                 e.codeBase = match("quoted string");
       
   431                 peekAndMatch(",");
       
   432             } else if (peekAndMatch("SignedBy")) {
       
   433                 if (e.signedBy != null)
       
   434                     throw new ParsingException(
       
   435                             st.lineno(),
       
   436                             LocalizedMessage.getMessage
       
   437                                 ("multiple.SignedBy.expressions"));
       
   438                 e.signedBy = match("quoted string");
       
   439 
       
   440                 // verify syntax of the aliases
       
   441                 StringTokenizer aliases = new StringTokenizer(e.signedBy,
       
   442                                                               ",", true);
       
   443                 int actr = 0;
       
   444                 int cctr = 0;
       
   445                 while (aliases.hasMoreTokens()) {
       
   446                     String alias = aliases.nextToken().trim();
       
   447                     if (alias.equals(","))
       
   448                         cctr++;
       
   449                     else if (alias.length() > 0)
       
   450                         actr++;
       
   451                 }
       
   452                 if (actr <= cctr)
       
   453                     throw new ParsingException(
       
   454                             st.lineno(),
       
   455                             LocalizedMessage.getMessage
       
   456                                 ("SignedBy.has.empty.alias"));
       
   457 
       
   458                 peekAndMatch(",");
       
   459             } else if (peekAndMatch("Principal")) {
       
   460                 if (principals == null) {
       
   461                     principals = new LinkedList<>();
       
   462                 }
       
   463 
       
   464                 String principalClass;
       
   465                 String principalName;
       
   466 
       
   467                 if (peek("\"")) {
       
   468                     // both the principalClass and principalName
       
   469                     // will be replaced later
       
   470                     principalClass = PrincipalEntry.REPLACE_NAME;
       
   471                     principalName = match("principal type");
       
   472                 } else {
       
   473                     // check for principalClass wildcard
       
   474                     if (peek("*")) {
       
   475                         match("*");
       
   476                         principalClass = PrincipalEntry.WILDCARD_CLASS;
       
   477                     } else {
       
   478                         principalClass = match("principal type");
       
   479                     }
       
   480 
       
   481                     // check for principalName wildcard
       
   482                     if (peek("*")) {
       
   483                         match("*");
       
   484                         principalName = PrincipalEntry.WILDCARD_NAME;
       
   485                     } else {
       
   486                         principalName = match("quoted string");
       
   487                     }
       
   488 
       
   489                     // disallow WILDCARD_CLASS && actual name
       
   490                     if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) &&
       
   491                         !principalName.equals(PrincipalEntry.WILDCARD_NAME)) {
       
   492                         if (debug != null) {
       
   493                                 debug.println("disallowing principal that " +
       
   494                                     "has WILDCARD class but no WILDCARD name");
       
   495                         }
       
   496                         throw new ParsingException
       
   497                                 (st.lineno(),
       
   498                                 LocalizedMessage.getMessage
       
   499                                     ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
       
   500                     }
       
   501                 }
       
   502 
       
   503                 try {
       
   504                     principalName = expand(principalName);
       
   505 
       
   506                     if (principalClass.equals
       
   507                                 ("javax.security.auth.x500.X500Principal") &&
       
   508                         !principalName.equals(PrincipalEntry.WILDCARD_NAME)) {
       
   509 
       
   510                         // 4702543:  X500 names with an EmailAddress
       
   511                         // were encoded incorrectly.  construct a new
       
   512                         // X500Principal with correct encoding.
       
   513 
       
   514                         X500Principal p = new X500Principal
       
   515                                 ((new X500Principal(principalName)).toString());
       
   516                         principalName = p.getName();
       
   517                     }
       
   518 
       
   519                     principals.add
       
   520                         (new PrincipalEntry(principalClass, principalName));
       
   521                 } catch (PropertyExpander.ExpandException peee) {
       
   522                     // ignore the entire policy entry
       
   523                     // but continue parsing all the info
       
   524                     // so we can get to the next entry
       
   525                     if (debug != null) {
       
   526                         debug.println("principal name expansion failed: " +
       
   527                                         principalName);
       
   528                     }
       
   529                     ignoreEntry = true;
       
   530                 }
       
   531                 peekAndMatch(",");
       
   532 
       
   533             } else {
       
   534                 throw new ParsingException(st.lineno(),
       
   535                     LocalizedMessage.getMessage
       
   536                         ("expected.codeBase.or.SignedBy.or.Principal"));
       
   537             }
       
   538         }
       
   539 
       
   540         if (principals != null) e.principals = principals;
       
   541         match("{");
       
   542 
       
   543         while(!peek("}")) {
       
   544             if (peek("Permission")) {
       
   545                 try {
       
   546                     PermissionEntry pe = parsePermissionEntry();
       
   547                     e.add(pe);
       
   548                 } catch (PropertyExpander.ExpandException peee) {
       
   549                     // ignore. The add never happened
       
   550                     if (debug != null) {
       
   551                         debug.println(peee.toString());
       
   552                     }
       
   553                     skipEntry();  // BugId 4219343
       
   554                 }
       
   555                 match(";");
       
   556             } else {
       
   557                 throw new
       
   558                     ParsingException(st.lineno(),
       
   559                         LocalizedMessage.getMessage
       
   560                             ("expected.permission.entry"));
       
   561             }
       
   562         }
       
   563         match("}");
       
   564 
       
   565         try {
       
   566             if (e.signedBy != null) e.signedBy = expand(e.signedBy);
       
   567             if (e.codeBase != null) {
       
   568                 e.codeBase = expand(e.codeBase, true).replace
       
   569                                     (File.separatorChar, '/');
       
   570             }
       
   571         } catch (PropertyExpander.ExpandException peee) {
       
   572             if (debug != null) {
       
   573                 debug.println(peee.toString());
       
   574             }
       
   575             return null;
       
   576         }
       
   577 
       
   578         return (ignoreEntry == true) ? null : e;
       
   579     }
       
   580 
       
   581     /**
       
   582      * parse a Permission entry
       
   583      */
       
   584     private PermissionEntry parsePermissionEntry()
       
   585         throws ParsingException, IOException, PropertyExpander.ExpandException
       
   586     {
       
   587         PermissionEntry e = new PermissionEntry();
       
   588 
       
   589         // Permission
       
   590         match("Permission");
       
   591         e.permission = match("permission type");
       
   592 
       
   593         if (peek("\"")) {
       
   594             // Permission name
       
   595             e.name = expand(match("quoted string"));
       
   596         }
       
   597 
       
   598         if (!peek(",")) {
       
   599             return e;
       
   600         }
       
   601         match(",");
       
   602 
       
   603         if (peek("\"")) {
       
   604                 e.action = expand(match("quoted string"));
       
   605                 if (!peek(",")) {
       
   606                     return e;
       
   607                 }
       
   608                 match(",");
       
   609         }
       
   610 
       
   611         if (peekAndMatch("SignedBy")) {
       
   612             e.signedBy = expand(match("quoted string"));
       
   613         }
       
   614         return e;
       
   615     }
       
   616 
       
   617     /**
       
   618      * parse a domain entry
       
   619      */
       
   620     private DomainEntry parseDomainEntry()
       
   621         throws ParsingException, IOException
       
   622     {
       
   623         boolean ignoreEntry = false;
       
   624         DomainEntry domainEntry;
       
   625         String name = null;
       
   626         Map<String, String> properties = new HashMap<>();
       
   627 
       
   628         match("domain");
       
   629         name = match("domain name");
       
   630 
       
   631         while(!peek("{")) {
       
   632             // get the domain properties
       
   633             properties = parseProperties("{");
       
   634         }
       
   635         match("{");
       
   636         domainEntry = new DomainEntry(name, properties);
       
   637 
       
   638         while(!peek("}")) {
       
   639 
       
   640             match("keystore");
       
   641             name = match("keystore name");
       
   642             // get the keystore properties
       
   643             if (!peek("}")) {
       
   644                 properties = parseProperties(";");
       
   645             }
       
   646             match(";");
       
   647             domainEntry.add(new KeyStoreEntry(name, properties));
       
   648         }
       
   649         match("}");
       
   650 
       
   651         return (ignoreEntry == true) ? null : domainEntry;
       
   652     }
       
   653 
       
   654     /*
       
   655      * Return a collection of domain properties or keystore properties.
       
   656      */
       
   657     private Map<String, String> parseProperties(String terminator)
       
   658         throws ParsingException, IOException {
       
   659 
       
   660         Map<String, String> properties = new HashMap<>();
       
   661         String key;
       
   662         String value;
       
   663         while (!peek(terminator)) {
       
   664             key = match("property name");
       
   665             match("=");
       
   666 
       
   667             try {
       
   668                 value = expand(match("quoted string"));
       
   669             } catch (PropertyExpander.ExpandException peee) {
       
   670                 throw new IOException(peee.getLocalizedMessage());
       
   671             }
       
   672             properties.put(key.toLowerCase(Locale.ENGLISH), value);
       
   673         }
       
   674 
       
   675         return properties;
       
   676     }
       
   677 
       
   678     private boolean peekAndMatch(String expect)
       
   679         throws ParsingException, IOException
       
   680     {
       
   681         if (peek(expect)) {
       
   682             match(expect);
       
   683             return true;
       
   684         } else {
       
   685             return false;
       
   686         }
       
   687     }
       
   688 
       
   689     private boolean peek(String expect) {
       
   690         boolean found = false;
       
   691 
       
   692         switch (lookahead) {
       
   693 
       
   694         case StreamTokenizer.TT_WORD:
       
   695             if (expect.equalsIgnoreCase(st.sval))
       
   696                 found = true;
       
   697             break;
       
   698         case ',':
       
   699             if (expect.equalsIgnoreCase(","))
       
   700                 found = true;
       
   701             break;
       
   702         case '{':
       
   703             if (expect.equalsIgnoreCase("{"))
       
   704                 found = true;
       
   705             break;
       
   706         case '}':
       
   707             if (expect.equalsIgnoreCase("}"))
       
   708                 found = true;
       
   709             break;
       
   710         case '"':
       
   711             if (expect.equalsIgnoreCase("\""))
       
   712                 found = true;
       
   713             break;
       
   714         case '*':
       
   715             if (expect.equalsIgnoreCase("*"))
       
   716                 found = true;
       
   717             break;
       
   718         case ';':
       
   719             if (expect.equalsIgnoreCase(";"))
       
   720                 found = true;
       
   721             break;
       
   722         default:
       
   723 
       
   724         }
       
   725         return found;
       
   726     }
       
   727 
       
   728     private String match(String expect)
       
   729         throws ParsingException, IOException
       
   730     {
       
   731         String value = null;
       
   732 
       
   733         switch (lookahead) {
       
   734         case StreamTokenizer.TT_NUMBER:
       
   735             throw new ParsingException(st.lineno(), expect,
       
   736                 LocalizedMessage.getMessage("number.") +
       
   737                     String.valueOf(st.nval));
       
   738         case StreamTokenizer.TT_EOF:
       
   739             LocalizedMessage localizedMsg = new LocalizedMessage
       
   740                 ("expected.expect.read.end.of.file.");
       
   741             Object[] source = {expect};
       
   742             String msg = "expected [" + expect + "], read [end of file]";
       
   743             throw new ParsingException(msg, localizedMsg, source);
       
   744         case StreamTokenizer.TT_WORD:
       
   745             if (expect.equalsIgnoreCase(st.sval)) {
       
   746                 lookahead = st.nextToken();
       
   747             } else if (expect.equalsIgnoreCase("permission type")) {
       
   748                 value = st.sval;
       
   749                 lookahead = st.nextToken();
       
   750             } else if (expect.equalsIgnoreCase("principal type")) {
       
   751                 value = st.sval;
       
   752                 lookahead = st.nextToken();
       
   753             } else if (expect.equalsIgnoreCase("domain name") ||
       
   754                        expect.equalsIgnoreCase("keystore name") ||
       
   755                        expect.equalsIgnoreCase("property name")) {
       
   756                 value = st.sval;
       
   757                 lookahead = st.nextToken();
       
   758             } else {
       
   759                  throw new ParsingException(st.lineno(), expect,
       
   760                                             st.sval);
       
   761             }
       
   762             break;
       
   763         case '"':
       
   764             if (expect.equalsIgnoreCase("quoted string")) {
       
   765                 value = st.sval;
       
   766                 lookahead = st.nextToken();
       
   767             } else if (expect.equalsIgnoreCase("permission type")) {
       
   768                 value = st.sval;
       
   769                 lookahead = st.nextToken();
       
   770             } else if (expect.equalsIgnoreCase("principal type")) {
       
   771                 value = st.sval;
       
   772                 lookahead = st.nextToken();
       
   773             } else {
       
   774                 throw new ParsingException(st.lineno(), expect, st.sval);
       
   775             }
       
   776             break;
       
   777         case ',':
       
   778             if (expect.equalsIgnoreCase(","))
       
   779                 lookahead = st.nextToken();
       
   780             else
       
   781                 throw new ParsingException(st.lineno(), expect, ",");
       
   782             break;
       
   783         case '{':
       
   784             if (expect.equalsIgnoreCase("{"))
       
   785                 lookahead = st.nextToken();
       
   786             else
       
   787                 throw new ParsingException(st.lineno(), expect, "{");
       
   788             break;
       
   789         case '}':
       
   790             if (expect.equalsIgnoreCase("}"))
       
   791                 lookahead = st.nextToken();
       
   792             else
       
   793                 throw new ParsingException(st.lineno(), expect, "}");
       
   794             break;
       
   795         case ';':
       
   796             if (expect.equalsIgnoreCase(";"))
       
   797                 lookahead = st.nextToken();
       
   798             else
       
   799                 throw new ParsingException(st.lineno(), expect, ";");
       
   800             break;
       
   801         case '*':
       
   802             if (expect.equalsIgnoreCase("*"))
       
   803                 lookahead = st.nextToken();
       
   804             else
       
   805                 throw new ParsingException(st.lineno(), expect, "*");
       
   806             break;
       
   807         case '=':
       
   808             if (expect.equalsIgnoreCase("="))
       
   809                 lookahead = st.nextToken();
       
   810             else
       
   811                 throw new ParsingException(st.lineno(), expect, "=");
       
   812             break;
       
   813         default:
       
   814             throw new ParsingException(st.lineno(), expect,
       
   815                                new String(new char[] {(char)lookahead}));
       
   816         }
       
   817         return value;
       
   818     }
       
   819 
       
   820     /**
       
   821      * skip all tokens for this entry leaving the delimiter ";"
       
   822      * in the stream.
       
   823      */
       
   824     private void skipEntry() throws ParsingException, IOException {
       
   825         while(lookahead != ';') {
       
   826             switch (lookahead) {
       
   827             case StreamTokenizer.TT_NUMBER:
       
   828                 throw new ParsingException(st.lineno(), ";",
       
   829                         LocalizedMessage.getMessage("number.") +
       
   830                             String.valueOf(st.nval));
       
   831             case StreamTokenizer.TT_EOF:
       
   832                 throw new ParsingException(LocalizedMessage.getMessage
       
   833                         ("expected.read.end.of.file."));
       
   834             default:
       
   835                 lookahead = st.nextToken();
       
   836             }
       
   837         }
       
   838     }
       
   839 
       
   840     /**
       
   841      * Each grant entry in the policy configuration file is
       
   842      * represented by a GrantEntry object.
       
   843      *
       
   844      * <p>
       
   845      * For example, the entry
       
   846      * <pre>
       
   847      *      grant signedBy "Duke" {
       
   848      *          permission java.io.FilePermission "/tmp", "read,write";
       
   849      *      };
       
   850      *
       
   851      * </pre>
       
   852      * is represented internally
       
   853      * <pre>
       
   854      *
       
   855      * pe = new PermissionEntry("java.io.FilePermission",
       
   856      *                           "/tmp", "read,write");
       
   857      *
       
   858      * ge = new GrantEntry("Duke", null);
       
   859      *
       
   860      * ge.add(pe);
       
   861      *
       
   862      * </pre>
       
   863      *
       
   864      * @author Roland Schemers
       
   865      *
       
   866      * version 1.19, 05/21/98
       
   867      */
       
   868 
       
   869     public static class GrantEntry {
       
   870 
       
   871         public String signedBy;
       
   872         public String codeBase;
       
   873         public LinkedList<PrincipalEntry> principals;
       
   874         public Vector<PermissionEntry> permissionEntries;
       
   875 
       
   876         public GrantEntry() {
       
   877             principals = new LinkedList<PrincipalEntry>();
       
   878             permissionEntries = new Vector<PermissionEntry>();
       
   879         }
       
   880 
       
   881         public GrantEntry(String signedBy, String codeBase) {
       
   882             this.codeBase = codeBase;
       
   883             this.signedBy = signedBy;
       
   884             principals = new LinkedList<PrincipalEntry>();
       
   885             permissionEntries = new Vector<PermissionEntry>();
       
   886         }
       
   887 
       
   888         public void add(PermissionEntry pe)
       
   889         {
       
   890             permissionEntries.addElement(pe);
       
   891         }
       
   892 
       
   893         public boolean remove(PrincipalEntry pe)
       
   894         {
       
   895             return principals.remove(pe);
       
   896         }
       
   897 
       
   898         public boolean remove(PermissionEntry pe)
       
   899         {
       
   900             return permissionEntries.removeElement(pe);
       
   901         }
       
   902 
       
   903         public boolean contains(PrincipalEntry pe)
       
   904         {
       
   905             return principals.contains(pe);
       
   906         }
       
   907 
       
   908         public boolean contains(PermissionEntry pe)
       
   909         {
       
   910             return permissionEntries.contains(pe);
       
   911         }
       
   912 
       
   913         /**
       
   914          * Enumerate all the permission entries in this GrantEntry.
       
   915          */
       
   916         public Enumeration<PermissionEntry> permissionElements(){
       
   917             return permissionEntries.elements();
       
   918         }
       
   919 
       
   920 
       
   921         public void write(PrintWriter out) {
       
   922             out.print("grant");
       
   923             if (signedBy != null) {
       
   924                 out.print(" signedBy \"");
       
   925                 out.print(signedBy);
       
   926                 out.print('"');
       
   927                 if (codeBase != null)
       
   928                     out.print(", ");
       
   929             }
       
   930             if (codeBase != null) {
       
   931                 out.print(" codeBase \"");
       
   932                 out.print(codeBase);
       
   933                 out.print('"');
       
   934                 if (principals != null && principals.size() > 0)
       
   935                     out.print(",\n");
       
   936             }
       
   937             if (principals != null && principals.size() > 0) {
       
   938                 Iterator<PrincipalEntry> pli = principals.iterator();
       
   939                 while (pli.hasNext()) {
       
   940                     out.print("      ");
       
   941                     PrincipalEntry pe = pli.next();
       
   942                     pe.write(out);
       
   943                     if (pli.hasNext())
       
   944                         out.print(",\n");
       
   945                 }
       
   946             }
       
   947             out.println(" {");
       
   948             Enumeration<PermissionEntry> enum_ = permissionEntries.elements();
       
   949             while (enum_.hasMoreElements()) {
       
   950                 PermissionEntry pe = enum_.nextElement();
       
   951                 out.write("  ");
       
   952                 pe.write(out);
       
   953             }
       
   954             out.println("};");
       
   955         }
       
   956 
       
   957         public Object clone() {
       
   958             GrantEntry ge = new GrantEntry();
       
   959             ge.codeBase = this.codeBase;
       
   960             ge.signedBy = this.signedBy;
       
   961             ge.principals = new LinkedList<PrincipalEntry>(this.principals);
       
   962             ge.permissionEntries =
       
   963                         new Vector<PermissionEntry>(this.permissionEntries);
       
   964             return ge;
       
   965         }
       
   966     }
       
   967 
       
   968     /**
       
   969      * Principal info (class and name) in a grant entry
       
   970      */
       
   971     public static class PrincipalEntry implements Principal {
       
   972 
       
   973         public static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS";
       
   974         public static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME";
       
   975         public static final String REPLACE_NAME = "PolicyParser.REPLACE_NAME";
       
   976 
       
   977         String principalClass;
       
   978         String principalName;
       
   979 
       
   980         /**
       
   981          * A PrincipalEntry consists of the Principal class and Principal name.
       
   982          *
       
   983          * @param principalClass the Principal class
       
   984          * @param principalName the Principal name
       
   985          * @throws NullPointerException if principalClass or principalName
       
   986          *                              are null
       
   987          */
       
   988         public PrincipalEntry(String principalClass, String principalName) {
       
   989             if (principalClass == null || principalName == null)
       
   990                 throw new NullPointerException(LocalizedMessage.getMessage
       
   991                     ("null.principalClass.or.principalName"));
       
   992             this.principalClass = principalClass;
       
   993             this.principalName = principalName;
       
   994         }
       
   995 
       
   996         boolean isWildcardName() {
       
   997             return principalName.equals(WILDCARD_NAME);
       
   998         }
       
   999 
       
  1000         boolean isWildcardClass() {
       
  1001             return principalClass.equals(WILDCARD_CLASS);
       
  1002         }
       
  1003 
       
  1004         boolean isReplaceName() {
       
  1005             return principalClass.equals(REPLACE_NAME);
       
  1006         }
       
  1007 
       
  1008         public String getPrincipalClass() {
       
  1009             return principalClass;
       
  1010         }
       
  1011 
       
  1012         public String getPrincipalName() {
       
  1013             return principalName;
       
  1014         }
       
  1015 
       
  1016         public String getDisplayClass() {
       
  1017             if (isWildcardClass()) {
       
  1018                 return "*";
       
  1019             } else if (isReplaceName()) {
       
  1020                 return "";
       
  1021             }
       
  1022             else return principalClass;
       
  1023         }
       
  1024 
       
  1025         public String getDisplayName() {
       
  1026             return getDisplayName(false);
       
  1027         }
       
  1028 
       
  1029         public String getDisplayName(boolean addQuote) {
       
  1030             if (isWildcardName()) {
       
  1031                 return "*";
       
  1032             }
       
  1033             else {
       
  1034                 if (addQuote) return "\"" + principalName + "\"";
       
  1035                 else return principalName;
       
  1036             }
       
  1037         }
       
  1038 
       
  1039         @Override
       
  1040         public String getName() {
       
  1041             return principalName;
       
  1042         }
       
  1043 
       
  1044         @Override
       
  1045         public String toString() {
       
  1046             if (!isReplaceName()) {
       
  1047                 return getDisplayClass() + "/" + getDisplayName();
       
  1048             } else {
       
  1049                 return getDisplayName();
       
  1050             }
       
  1051         }
       
  1052 
       
  1053         /**
       
  1054          * Test for equality between the specified object and this object.
       
  1055          * Two PrincipalEntries are equal if their class and name values
       
  1056          * are equal.
       
  1057          *
       
  1058          * @param obj the object to test for equality with this object
       
  1059          * @return true if the objects are equal, false otherwise
       
  1060          */
       
  1061         @Override
       
  1062         public boolean equals(Object obj) {
       
  1063             if (this == obj)
       
  1064                 return true;
       
  1065 
       
  1066             if (!(obj instanceof PrincipalEntry))
       
  1067                 return false;
       
  1068 
       
  1069             PrincipalEntry that = (PrincipalEntry)obj;
       
  1070             return (principalClass.equals(that.principalClass) &&
       
  1071                     principalName.equals(that.principalName));
       
  1072         }
       
  1073 
       
  1074         /**
       
  1075          * Return a hashcode for this PrincipalEntry.
       
  1076          *
       
  1077          * @return a hashcode for this PrincipalEntry
       
  1078          */
       
  1079         @Override
       
  1080         public int hashCode() {
       
  1081             return principalClass.hashCode();
       
  1082         }
       
  1083 
       
  1084         public void write(PrintWriter out) {
       
  1085             out.print("principal " + getDisplayClass() + " " +
       
  1086                       getDisplayName(true));
       
  1087         }
       
  1088     }
       
  1089 
       
  1090     /**
       
  1091      * Each permission entry in the policy configuration file is
       
  1092      * represented by a
       
  1093      * PermissionEntry object.
       
  1094      *
       
  1095      * <p>
       
  1096      * For example, the entry
       
  1097      * <pre>
       
  1098      *          permission java.io.FilePermission "/tmp", "read,write";
       
  1099      * </pre>
       
  1100      * is represented internally
       
  1101      * <pre>
       
  1102      *
       
  1103      * pe = new PermissionEntry("java.io.FilePermission",
       
  1104      *                           "/tmp", "read,write");
       
  1105      * </pre>
       
  1106      *
       
  1107      * @author Roland Schemers
       
  1108      *
       
  1109      * version 1.19, 05/21/98
       
  1110      */
       
  1111 
       
  1112     public static class PermissionEntry {
       
  1113 
       
  1114         public String permission;
       
  1115         public String name;
       
  1116         public String action;
       
  1117         public String signedBy;
       
  1118 
       
  1119         public PermissionEntry() {
       
  1120         }
       
  1121 
       
  1122         public PermissionEntry(String permission,
       
  1123                         String name,
       
  1124                         String action) {
       
  1125             this.permission = permission;
       
  1126             this.name = name;
       
  1127             this.action = action;
       
  1128         }
       
  1129 
       
  1130         /**
       
  1131          * Calculates a hash code value for the object.  Objects
       
  1132          * which are equal will also have the same hashcode.
       
  1133          */
       
  1134         @Override
       
  1135         public int hashCode() {
       
  1136             int retval = permission.hashCode();
       
  1137             if (name != null) retval ^= name.hashCode();
       
  1138             if (action != null) retval ^= action.hashCode();
       
  1139             return retval;
       
  1140         }
       
  1141 
       
  1142         @Override
       
  1143         public boolean equals(Object obj) {
       
  1144             if (obj == this)
       
  1145                 return true;
       
  1146 
       
  1147             if (! (obj instanceof PermissionEntry))
       
  1148                 return false;
       
  1149 
       
  1150             PermissionEntry that = (PermissionEntry) obj;
       
  1151 
       
  1152             if (this.permission == null) {
       
  1153                 if (that.permission != null) return false;
       
  1154             } else {
       
  1155                 if (!this.permission.equals(that.permission)) return false;
       
  1156             }
       
  1157 
       
  1158             if (this.name == null) {
       
  1159                 if (that.name != null) return false;
       
  1160             } else {
       
  1161                 if (!this.name.equals(that.name)) return false;
       
  1162             }
       
  1163 
       
  1164             if (this.action == null) {
       
  1165                 if (that.action != null) return false;
       
  1166             } else {
       
  1167                 if (!this.action.equals(that.action)) return false;
       
  1168             }
       
  1169 
       
  1170             if (this.signedBy == null) {
       
  1171                 if (that.signedBy != null) return false;
       
  1172             } else {
       
  1173                 if (!this.signedBy.equals(that.signedBy)) return false;
       
  1174             }
       
  1175 
       
  1176             // everything matched -- the 2 objects are equal
       
  1177             return true;
       
  1178         }
       
  1179 
       
  1180         public void write(PrintWriter out) {
       
  1181             out.print("permission ");
       
  1182             out.print(permission);
       
  1183             if (name != null) {
       
  1184                 out.print(" \"");
       
  1185 
       
  1186                 // ATTENTION: regex with double escaping,
       
  1187                 // the normal forms look like:
       
  1188                 // $name =~ s/\\/\\\\/g; and
       
  1189                 // $name =~ s/\"/\\\"/g;
       
  1190                 // and then in a java string, it's escaped again
       
  1191 
       
  1192                 out.print(name.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\\\""));
       
  1193                 out.print('"');
       
  1194             }
       
  1195             if (action != null) {
       
  1196                 out.print(", \"");
       
  1197                 out.print(action);
       
  1198                 out.print('"');
       
  1199             }
       
  1200             if (signedBy != null) {
       
  1201                 out.print(", signedBy \"");
       
  1202                 out.print(signedBy);
       
  1203                 out.print('"');
       
  1204             }
       
  1205             out.println(";");
       
  1206         }
       
  1207     }
       
  1208 
       
  1209     /**
       
  1210      * Each domain entry in the keystore domain configuration file is
       
  1211      * represented by a DomainEntry object.
       
  1212      */
       
  1213     static class DomainEntry {
       
  1214         private final String name;
       
  1215         private final Map<String, String> properties;
       
  1216         private final Map<String, KeyStoreEntry> entries;
       
  1217 
       
  1218         DomainEntry(String name, Map<String, String> properties) {
       
  1219             this.name = name;
       
  1220             this.properties = properties;
       
  1221             entries = new HashMap<>();
       
  1222         }
       
  1223 
       
  1224         String getName() {
       
  1225             return name;
       
  1226         }
       
  1227 
       
  1228         Map<String, String> getProperties() {
       
  1229             return properties;
       
  1230         }
       
  1231 
       
  1232         Collection<KeyStoreEntry> getEntries() {
       
  1233             return entries.values();
       
  1234         }
       
  1235 
       
  1236         void add(KeyStoreEntry entry) throws ParsingException {
       
  1237             String keystoreName = entry.getName();
       
  1238             if (!entries.containsKey(keystoreName)) {
       
  1239                 entries.put(keystoreName, entry);
       
  1240             } else {
       
  1241                 LocalizedMessage localizedMsg = new LocalizedMessage
       
  1242                     ("duplicate.keystore.name");
       
  1243                 Object[] source = {keystoreName};
       
  1244                 String msg = "duplicate keystore name: " + keystoreName;
       
  1245                 throw new ParsingException(msg, localizedMsg, source);
       
  1246             }
       
  1247         }
       
  1248 
       
  1249         @Override
       
  1250         public String toString() {
       
  1251             StringBuilder s =
       
  1252                 new StringBuilder("\ndomain ").append(name);
       
  1253 
       
  1254             if (properties != null) {
       
  1255                 for (Map.Entry<String, String> property :
       
  1256                     properties.entrySet()) {
       
  1257                     s.append("\n        ").append(property.getKey()).append('=')
       
  1258                         .append(property.getValue());
       
  1259                 }
       
  1260             }
       
  1261             s.append(" {\n");
       
  1262 
       
  1263             if (entries != null) {
       
  1264                 for (KeyStoreEntry entry : entries.values()) {
       
  1265                     s.append(entry).append("\n");
       
  1266                 }
       
  1267             }
       
  1268             s.append("}");
       
  1269 
       
  1270             return s.toString();
       
  1271         }
       
  1272     }
       
  1273 
       
  1274     /**
       
  1275      * Each keystore entry in the keystore domain configuration file is
       
  1276      * represented by a KeyStoreEntry object.
       
  1277      */
       
  1278 
       
  1279     static class KeyStoreEntry {
       
  1280         private final String name;
       
  1281         private final Map<String, String> properties;
       
  1282 
       
  1283         KeyStoreEntry(String name, Map<String, String> properties) {
       
  1284             this.name = name;
       
  1285             this.properties = properties;
       
  1286         }
       
  1287 
       
  1288         String getName() {
       
  1289             return name;
       
  1290         }
       
  1291 
       
  1292         Map<String, String>  getProperties() {
       
  1293             return properties;
       
  1294         }
       
  1295 
       
  1296         @Override
       
  1297         public String toString() {
       
  1298             StringBuilder s = new StringBuilder("\n    keystore ").append(name);
       
  1299             if (properties != null) {
       
  1300                 for (Map.Entry<String, String> property :
       
  1301                     properties.entrySet()) {
       
  1302                     s.append("\n        ").append(property.getKey()).append('=')
       
  1303                         .append(property.getValue());
       
  1304                 }
       
  1305             }
       
  1306             s.append(";");
       
  1307 
       
  1308             return s.toString();
       
  1309         }
       
  1310     }
       
  1311 
       
  1312     public static class ParsingException extends GeneralSecurityException {
       
  1313 
       
  1314         private static final long serialVersionUID = -4330692689482574072L;
       
  1315 
       
  1316         private String i18nMessage;
       
  1317         private LocalizedMessage localizedMsg;
       
  1318         private Object[] source;
       
  1319 
       
  1320         /**
       
  1321          * Constructs a ParsingException with the specified
       
  1322          * detail message. A detail message is a String that describes
       
  1323          * this particular exception, which may, for example, specify which
       
  1324          * algorithm is not available.
       
  1325          *
       
  1326          * @param msg the detail message.
       
  1327          */
       
  1328         public ParsingException(String msg) {
       
  1329             super(msg);
       
  1330             i18nMessage = msg;
       
  1331         }
       
  1332 
       
  1333         public ParsingException(String msg, LocalizedMessage localizedMsg,
       
  1334                                 Object[] source) {
       
  1335             super(msg);
       
  1336             this.localizedMsg = localizedMsg;
       
  1337             this.source = source;
       
  1338         }
       
  1339 
       
  1340         public ParsingException(int line, String msg) {
       
  1341             super("line " + line + ": " + msg);
       
  1342             // don't call form.format unless getLocalizedMessage is called
       
  1343             // to avoid unnecessary permission checks
       
  1344             localizedMsg = new LocalizedMessage("line.number.msg");
       
  1345             source = new Object[] {line, msg};
       
  1346         }
       
  1347 
       
  1348         public ParsingException(int line, String expect, String actual) {
       
  1349             super("line " + line + ": expected [" + expect +
       
  1350                 "], found [" + actual + "]");
       
  1351             // don't call form.format unless getLocalizedMessage is called
       
  1352             // to avoid unnecessary permission checks
       
  1353             localizedMsg = new LocalizedMessage
       
  1354                 ("line.number.expected.expect.found.actual.");
       
  1355             source = new Object[] {line, expect, actual};
       
  1356         }
       
  1357 
       
  1358         @Override
       
  1359         public String getLocalizedMessage() {
       
  1360             return i18nMessage != null ? i18nMessage : localizedMsg.format(source);
       
  1361         }
       
  1362     }
       
  1363 
       
  1364     public static void main(String[] arg) throws Exception {
       
  1365         try (FileReader fr = new FileReader(arg[0]);
       
  1366              FileWriter fw = new FileWriter(arg[1])) {
       
  1367             PolicyParser pp = new PolicyParser(true);
       
  1368             pp.read(fr);
       
  1369             pp.write(fw);
       
  1370         }
       
  1371     }
       
  1372 }