001    /* FilePermission.java --
002       Copyright (C) 1998, 2000, 2003, 2004, 2005, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.io;
041    
042    import java.security.Permission;
043    
044    public final class FilePermission extends Permission implements Serializable
045    {
046      private static final long serialVersionUID = 7930732926638008763L;
047    
048      private static final String ALL_FILES = "<<ALL FILES>>";
049    
050      private boolean readPerm = false;
051      private boolean writePerm = false;
052      private boolean executePerm = false;
053      private boolean deletePerm = false;
054      private final String actionsString;
055    
056      // Checks and caches the actions
057      private void checkPerms() throws IllegalArgumentException
058      {
059        String action;
060        int i = actionsString.indexOf(',');
061        int startI = 0;
062        while (i != -1) 
063          {
064            action = actionsString.substring(startI, i).trim().toLowerCase();
065            if (action.equals("read"))
066              readPerm = true;
067            else if (action.equals("write"))
068              writePerm = true;
069            else if (action.equals("execute"))
070              executePerm = true;
071            else if (action.equals("delete"))
072              deletePerm = true;
073            else
074              throw new IllegalArgumentException("Unknown action: " + action);
075          
076            startI = i + 1;
077            i = actionsString.indexOf(',', startI);
078          }
079    
080        action = actionsString.substring(startI).trim().toLowerCase();
081        if (action.equals("read"))
082          readPerm = true;
083        else if (action.equals("write"))
084          writePerm = true;
085        else if (action.equals("execute"))
086          executePerm = true;
087        else if (action.equals("delete"))
088          deletePerm = true;
089        else
090          throw new IllegalArgumentException("Unknown action: " + action);
091      }
092    
093      /**
094       * Create a new FilePermission.
095       *
096       * @param pathExpression an expression specifying the paths this
097       *        permission represents.
098       * @param actionsString a comma-separated list of the actions this
099       *        permission represents. The actions must be "read", "write",
100       *        "execute" and/or "delete".
101       */
102      public FilePermission(String pathExpression, String actionsString) 
103      {
104        // FIXME: what to do when the file string is malformed?
105        super(pathExpression);
106        if (pathExpression == null)
107          throw new NullPointerException("pathExpression");
108        if (actionsString == null)
109          throw new IllegalArgumentException("actionsString");
110        this.actionsString = actionsString;
111        checkPerms();
112      }
113      
114      /**
115       * Get the actions this FilePermission supports.
116       * @return the String representing the actions this FilePermission supports.
117       */
118      public String getActions() 
119      {
120        return actionsString;
121      }
122    
123      /**
124       * Get the hash code for this Object.<P>
125       * FilePermission's hash code is calculated as the exclusive or of the 
126       * target
127       * String's hash code and the action String's hash code.
128       * @specnote Sun did not specify how to calculate the hash code; 
129       * I made this up.
130       * @return the hash code for this Object.
131       */
132      public int hashCode() 
133      {
134        return getName().hashCode() ^ actionsString.hashCode();
135      }
136    
137      /**
138       * Check two FilePermissions for semantic equality.
139       * Two FilePermissions are exactly equivalent if they have identical path
140       * expressions and have exactly the same access permissions.
141       * @param o the Object to compare to.
142       * @return whether the Objects are semantically equivalent.
143       */
144      public boolean equals(Object o) 
145      {
146        if (! (o instanceof FilePermission))
147          return false;
148        FilePermission p = (FilePermission) o;
149    
150        String f1 = getName();
151        String f2 = p.getName();
152    
153        // Compare names, taking into account if they refer to a directory
154        // and one has a separator and the other does not.
155        if (f1.length() > 0 && f1.charAt(f1.length() - 1) == File.separatorChar) 
156          {
157            if (f2.length() > 0
158                && f2.charAt(f2.length() - 1) == File.separatorChar) 
159              {
160                if (! f2.equals(f1))
161                  return false;
162              }
163            else
164              {
165                if (! f2.equals(f1.substring(0, f1.length() - 1)))
166                  return false;
167              }
168          }
169        else
170          {
171            if (f2.length() > 0
172                && f2.charAt(f2.length() - 1) == File.separatorChar)
173              {
174                if (! f1.equals(f2.substring(0, f2.length() - 1)))
175                  return false;
176              }
177            else
178              {
179                if (! f1.equals(f2))
180                  return false;
181              }
182          }
183        return (readPerm == p.readPerm
184                && writePerm == p.writePerm
185                && executePerm == p.executePerm
186                && deletePerm == p.deletePerm);
187      }
188    
189      /**
190       * Check to see if this permission implies another.
191       * Permission A implies permission B if these things are all true:
192       * <OL>
193       * <LI>A and B are both FilePermissions.</LI>
194       * <LI>All possible files in B are included in A 
195       * (possibly more are in A).</LI>
196       * <LI>All actions B supports, A also supports.</LI>
197       * </OL>
198       * @param p the Permission to compare against.
199       * @return whether this Permission implies p
200       */
201      public boolean implies(Permission p) 
202      {
203        if (! (p instanceof FilePermission))
204          return false;
205    
206        String f1 = getName();
207    
208        if (f1.equals(ALL_FILES))
209          return true;
210        
211        FilePermission fp = (FilePermission) p;
212        String f2 = fp.getName();
213    
214        if (f2.equals(ALL_FILES))
215          return false;
216    
217        try
218          {
219            f1 = new File(f1).getCanonicalPath();
220            f2 = new File(f2).getCanonicalPath();
221          }
222        catch (IOException ioe)
223          {
224            return false;
225          }
226    
227        String sub1;
228    
229        switch (f1.charAt(f1.length() - 1))
230          {
231          case '*':
232            sub1 = f1.substring(0, f1.length() - 1); // chop off "*"
233            if (f2.length() <= sub1.length())
234              {
235                // If it's smaller, there is no way it could be part of
236                // this directory.  If it's the same (or length - 1), it
237                // could be the same directory but specifies access to
238                // the directory rather than the files in it.
239                return false;
240              } 
241            else if (f2.charAt(sub1.length() - 1) == File.separatorChar)
242              {
243                // Make sure the part before the "/" is the same.
244                if (! f2.substring(0, sub1.length()).equals(sub1))
245                  return false;
246                // Make sure there are no subdirectories specified
247                // underneath this one.
248                if (f2.substring(sub1.length() + 1).indexOf(File.separatorChar)
249                    != -1)
250                  return false;
251              }
252            else
253              {
254                // Obviously not equal: f2 is either not a directory or
255                // is not the same directory (its name continues further
256                // than we want).
257                return false;
258              }
259            break;
260          case '-':
261            // Chop off "/-".
262            sub1 = f1.substring(0, f1.length() - 2);
263            if (f2.length() < sub1.length())
264              {
265                // If it's smaller, there is no way it could be part of
266                // this directory.
267                return false;
268              }
269            else if (f2.length() > sub1.length()
270                     && f2.charAt(sub1.length()) != File.separatorChar)
271              return false;
272            else if (! f2.substring(0, sub1.length()).equals(sub1))
273              return false;
274            break;
275    
276          default:
277            if (!f1.equals(f2))
278              return false;
279            break;
280          }
281    
282        if (fp.readPerm && ! readPerm)
283          return false;
284        if (fp.writePerm && ! writePerm)
285          return false;
286        if (fp.executePerm && ! executePerm)
287          return false;
288        if (fp.deletePerm && ! deletePerm)
289          return false;
290        
291        return true;
292      }
293    }