diff --git a/openjdk/allsources.lst b/openjdk/allsources.lst index 4005d21e..21b396ca 100644 --- a/openjdk/allsources.lst +++ b/openjdk/allsources.lst @@ -1042,6 +1042,7 @@ java/io/DeleteOnExitHook.java java/io/FileDescriptor.java java/io/FileInputStream.java java/io/FileOutputStream.java +java/io/FilePermission.java java/io/ObjectStreamClass.java java/io/RandomAccessFile.java java/lang/ApplicationShutdownHooks.java @@ -1101,8 +1102,10 @@ sun/misc/SharedSecrets.java sun/misc/Unsafe.java sun/misc/Version.java sun/misc/VM.java +sun/net/dns/ResolverConfigurationImpl.java sun/net/www/protocol/ikvmres/Handler.java sun/net/www/protocol/file/Handler.java +sun/net/www/protocol/file/FileURLConnection.java sun/net/www/protocol/jar/JarFileFactory.java sun/nio/ch/DatagramChannelImpl.java sun/nio/ch/DefaultSelectorProvider.java @@ -7503,7 +7506,6 @@ SystemProperties.java ../../openjdk6-b12/jdk/src/share/classes/java/io/FileFilter.java ../../openjdk6-b12/jdk/src/share/classes/java/io/FilenameFilter.java ../../openjdk6-b12/jdk/src/share/classes/java/io/FileNotFoundException.java -../../openjdk6-b12/jdk/src/share/classes/java/io/FilePermission.java ../../openjdk6-b12/jdk/src/share/classes/java/io/FileReader.java ../../openjdk6-b12/jdk/src/share/classes/java/io/FileSystem.java ../../openjdk6-b12/jdk/src/share/classes/java/io/FileWriter.java @@ -9750,7 +9752,6 @@ SystemProperties.java ../../openjdk6-b12/jdk/src/share/classes/sun/net/www/http/KeepAliveStream.java ../../openjdk6-b12/jdk/src/share/classes/sun/net/www/http/KeepAliveStreamCleaner.java ../../openjdk6-b12/jdk/src/share/classes/sun/net/www/http/PosterOutputStream.java -../../openjdk6-b12/jdk/src/share/classes/sun/net/www/protocol/file/FileURLConnection.java ../../openjdk6-b12/jdk/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java ../../openjdk6-b12/jdk/src/share/classes/sun/net/www/protocol/ftp/Handler.java ../../openjdk6-b12/jdk/src/share/classes/sun/net/www/protocol/gopher/GopherClient.java @@ -10897,7 +10898,6 @@ SystemProperties.java ../../openjdk6-b12/jdk/src/windows/classes/java/lang/Terminator.java ../../openjdk6-b12/jdk/src/windows/classes/java/util/prefs/WindowsPreferences.java ../../openjdk6-b12/jdk/src/windows/classes/java/util/prefs/WindowsPreferencesFactory.java -../../openjdk6-b12/jdk/src/windows/classes/sun/net/dns/ResolverConfigurationImpl.java ../../openjdk6-b12/jdk/src/windows/classes/sun/nio/ch/NativeThread.java ../../openjdk6-b12/jdk/src/windows/classes/sun/nio/ch/PipeImpl.java ../../openjdk6-b12/jdk/src/windows/classes/sun/nio/ch/SinkChannelImpl.java diff --git a/openjdk/java/io/FilePermission.java b/openjdk/java/io/FilePermission.java new file mode 100644 index 00000000..33ffad64 --- /dev/null +++ b/openjdk/java/io/FilePermission.java @@ -0,0 +1,839 @@ +/* + * Copyright 1997-2005 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.io; + +import java.security.*; +import java.util.Enumeration; +import java.util.List; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.Collections; +import java.io.ObjectStreamField; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.IOException; +import sun.security.util.SecurityConstants; + +/** + * This class represents access to a file or directory. A FilePermission consists + * of a pathname and a set of actions valid for that pathname. + *

+ * Pathname is the pathname of the file or directory granted the specified + * actions. A pathname that ends in "/*" (where "/" is + * the file separator character, File.separatorChar) indicates + * all the files and directories contained in that directory. A pathname + * that ends with "/-" indicates (recursively) all files + * and subdirectories contained in that directory. A pathname consisting of + * the special token "<<ALL FILES>>" matches any file. + *

+ * Note: A pathname consisting of a single "*" indicates all the files + * in the current directory, while a pathname consisting of a single "-" + * indicates all the files in the current directory and + * (recursively) all files and subdirectories contained in the current + * directory. + *

+ * The actions to be granted are passed to the constructor in a string containing + * a list of one or more comma-separated keywords. The possible keywords are + * "read", "write", "execute", and "delete". Their meaning is defined as follows: + *

+ *

+ *
read
read permission + *
write
write permission + *
execute + *
execute permission. Allows Runtime.exec to + * be called. Corresponds to SecurityManager.checkExec. + *
delete + *
delete permission. Allows File.delete to + * be called. Corresponds to SecurityManager.checkDelete. + *
+ *

+ * The actions string is converted to lowercase before processing. + *

+ * Be careful when granting FilePermissions. Think about the implications + * of granting read and especially write access to various files and + * directories. The "<<ALL FILES>>" permission with write action is + * especially dangerous. This grants permission to write to the entire + * file system. One thing this effectively allows is replacement of the + * system binary, including the JVM runtime environment. + * + *

Please note: Code can always read a file from the same + * directory it's in (or a subdirectory of that directory); it does not + * need explicit permission to do so. + * + * @see java.security.Permission + * @see java.security.Permissions + * @see java.security.PermissionCollection + * + * + * @author Marianne Mueller + * @author Roland Schemers + * @since 1.2 + * + * @serial exclude + */ + +public final class FilePermission extends Permission implements Serializable { + + /** + * Execute action. + */ + private final static int EXECUTE = 0x1; + /** + * Write action. + */ + private final static int WRITE = 0x2; + /** + * Read action. + */ + private final static int READ = 0x4; + /** + * Delete action. + */ + private final static int DELETE = 0x8; + + /** + * All actions (read,write,execute,delete) + */ + private final static int ALL = READ|WRITE|EXECUTE|DELETE; + /** + * No actions. + */ + private final static int NONE = 0x0; + + // the actions mask + private transient int mask; + + // does path indicate a directory? (wildcard or recursive) + private transient boolean directory; + + // is it a recursive directory specification? + private transient boolean recursive; + + /** + * the actions string. + * + * @serial + */ + private String actions; // Left null as long as possible, then + // created and re-used in the getAction function. + + // canonicalized dir path. In the case of + // directories, it is the name "/blah/*" or "/blah/-" without + // the last character (the "*" or "-"). + + private transient String cpath; + + // static Strings used by init(int mask) + private static final char RECURSIVE_CHAR = '-'; + private static final char WILD_CHAR = '*'; + +/* + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("***\n"); + sb.append("cpath = "+cpath+"\n"); + sb.append("mask = "+mask+"\n"); + sb.append("actions = "+getActions()+"\n"); + sb.append("directory = "+directory+"\n"); + sb.append("recursive = "+recursive+"\n"); + sb.append("***\n"); + return sb.toString(); + } +*/ + + private static final long serialVersionUID = 7930732926638008763L; + + /** + * initialize a FilePermission object. Common to all constructors. + * Also called during de-serialization. + * + * @param mask the actions mask to use. + * + */ + private void init(int mask) + { + + if ((mask & ALL) != mask) + throw new IllegalArgumentException("invalid actions mask"); + + if (mask == NONE) + throw new IllegalArgumentException("invalid actions mask"); + + if ((cpath = getName()) == null) + throw new NullPointerException("name can't be null"); + + this.mask = mask; + + if (cpath.equals("<>")) { + directory = true; + recursive = true; + cpath = ""; + return; + } + + // store only the canonical cpath if possible + cpath = AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + try { + return canonPath(cpath); + } catch (IOException ioe) { + return cpath; + } + } + }); + + int len = cpath.length(); + char last = ((len > 0) ? cpath.charAt(len - 1) : 0); + + if (last == RECURSIVE_CHAR && + cpath.charAt(len - 2) == File.separatorChar) { + directory = true; + recursive = true; + cpath = cpath.substring(0, --len); + } else if (last == WILD_CHAR && + cpath.charAt(len - 2) == File.separatorChar) { + directory = true; + //recursive = false; + cpath = cpath.substring(0, --len); + } else { + // overkill since they are initialized to false, but + // commented out here to remind us... + //directory = false; + //recursive = false; + } + + // XXX: at this point the path should be absolute. die if it isn't? + } + + // IKVM copied from sun.security.provider.PolicyFile to remove unnecessary dependency + private static String canonPath(String path) throws IOException { + + if (path.endsWith("*")) { + + path = path.substring(0, path.length()-1) + "-"; + + path = new File(path).getCanonicalPath(); + + return path.substring(0, path.length()-1) + "*"; + + } else { + + return new File(path).getCanonicalPath(); + + } + + } + + + /** + * Creates a new FilePermission object with the specified actions. + * path is the pathname of a file or directory, and actions + * contains a comma-separated list of the desired actions granted on the + * file or directory. Possible actions are + * "read", "write", "execute", and "delete". + * + *

A pathname that ends in "/*" (where "/" is + * the file separator character, File.separatorChar) + * indicates all the files and directories contained in that directory. + * A pathname that ends with "/-" indicates (recursively) all files and + * subdirectories contained in that directory. The special pathname + * "<<ALL FILES>>" matches any file. + * + *

A pathname consisting of a single "*" indicates all the files + * in the current directory, while a pathname consisting of a single "-" + * indicates all the files in the current directory and + * (recursively) all files and subdirectories contained in the current + * directory. + * + *

A pathname containing an empty string represents an empty path. + * + * @param path the pathname of the file/directory. + * @param actions the action string. + * + * @throws IllegalArgumentException + * If actions is null, empty or contains an action + * other than the specified possible actions. + */ + + public FilePermission(String path, String actions) + { + super(path); + init(getMask(actions)); + } + + /** + * Creates a new FilePermission object using an action mask. + * More efficient than the FilePermission(String, String) constructor. + * Can be used from within + * code that needs to create a FilePermission object to pass into the + * implies method. + * + * @param path the pathname of the file/directory. + * @param mask the action mask to use. + */ + + // package private for use by the FilePermissionCollection add method + FilePermission(String path, int mask) + { + super(path); + init(mask); + } + + /** + * Checks if this FilePermission object "implies" the specified permission. + *

+ * More specifically, this method returns true if:

+ *

+ * + * @param p the permission to check against. + * + * @return true if the specified permission is not + * null and is implied by this object, + * false otherwise. + */ + public boolean implies(Permission p) { + if (!(p instanceof FilePermission)) + return false; + + FilePermission that = (FilePermission) p; + + // we get the effective mask. i.e., the "and" of this and that. + // They must be equal to that.mask for implies to return true. + + return ((this.mask & that.mask) == that.mask) && impliesIgnoreMask(that); + } + + /** + * Checks if the Permission's actions are a proper subset of the + * this object's actions. Returns the effective mask iff the + * this FilePermission's path also implies that FilePermission's path. + * + * @param that the FilePermission to check against. + * @param exact return immediately if the masks are not equal + * @return the effective mask + */ + boolean impliesIgnoreMask(FilePermission that) { + if (this.directory) { + if (this.recursive) { + // make sure that.path is longer then path so + // something like /foo/- does not imply /foo + if (that.directory) { + return (that.cpath.length() >= this.cpath.length()) && + that.cpath.startsWith(this.cpath); + } else { + return ((that.cpath.length() > this.cpath.length()) && + that.cpath.startsWith(this.cpath)); + } + } else { + if (that.directory) { + // if the permission passed in is a directory + // specification, make sure that a non-recursive + // permission (i.e., this object) can't imply a recursive + // permission. + if (that.recursive) + return false; + else + return (this.cpath.equals(that.cpath)); + } else { + int last = that.cpath.lastIndexOf(File.separatorChar); + if (last == -1) + return false; + else { + // this.cpath.equals(that.cpath.substring(0, last+1)); + // Use regionMatches to avoid creating new string + return (this.cpath.length() == (last + 1)) && + this.cpath.regionMatches(0, that.cpath, 0, last+1); + } + } + } + } else if (that.directory) { + // if this is NOT recursive/wildcarded, + // do not let it imply a recursive/wildcarded permission + return false; + } else { + return (this.cpath.equals(that.cpath)); + } + } + + /** + * Checks two FilePermission objects for equality. Checks that obj is + * a FilePermission, and has the same pathname and actions as this object. + *

+ * @param obj the object we are testing for equality with this object. + * @return true if obj is a FilePermission, and has the same + * pathname and actions as this FilePermission object, + * false otherwise. + */ + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (! (obj instanceof FilePermission)) + return false; + + FilePermission that = (FilePermission) obj; + + return (this.mask == that.mask) && + this.cpath.equals(that.cpath) && + (this.directory == that.directory) && + (this.recursive == that.recursive); + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + + public int hashCode() { + return this.cpath.hashCode(); + } + + /** + * Converts an actions String to an actions mask. + * + * @param action the action string. + * @return the actions mask. + */ + private static int getMask(String actions) { + + int mask = NONE; + + // Null action valid? + if (actions == null) { + return mask; + } + // Check against use of constants (used heavily within the JDK) + if (actions == SecurityConstants.FILE_READ_ACTION) { + return READ; + } else if (actions == SecurityConstants.FILE_WRITE_ACTION) { + return WRITE; + } else if (actions == SecurityConstants.FILE_EXECUTE_ACTION) { + return EXECUTE; + } else if (actions == SecurityConstants.FILE_DELETE_ACTION) { + return DELETE; + } + + char[] a = actions.toCharArray(); + + int i = a.length - 1; + if (i < 0) + return mask; + + while (i != -1) { + char c; + + // skip whitespace + while ((i!=-1) && ((c = a[i]) == ' ' || + c == '\r' || + c == '\n' || + c == '\f' || + c == '\t')) + i--; + + // check for the known strings + int matchlen; + + if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') && + (a[i-2] == 'e' || a[i-2] == 'E') && + (a[i-1] == 'a' || a[i-1] == 'A') && + (a[i] == 'd' || a[i] == 'D')) + { + matchlen = 4; + mask |= READ; + + } else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') && + (a[i-3] == 'r' || a[i-3] == 'R') && + (a[i-2] == 'i' || a[i-2] == 'I') && + (a[i-1] == 't' || a[i-1] == 'T') && + (a[i] == 'e' || a[i] == 'E')) + { + matchlen = 5; + mask |= WRITE; + + } else if (i >= 6 && (a[i-6] == 'e' || a[i-6] == 'E') && + (a[i-5] == 'x' || a[i-5] == 'X') && + (a[i-4] == 'e' || a[i-4] == 'E') && + (a[i-3] == 'c' || a[i-3] == 'C') && + (a[i-2] == 'u' || a[i-2] == 'U') && + (a[i-1] == 't' || a[i-1] == 'T') && + (a[i] == 'e' || a[i] == 'E')) + { + matchlen = 7; + mask |= EXECUTE; + + } else if (i >= 5 && (a[i-5] == 'd' || a[i-5] == 'D') && + (a[i-4] == 'e' || a[i-4] == 'E') && + (a[i-3] == 'l' || a[i-3] == 'L') && + (a[i-2] == 'e' || a[i-2] == 'E') && + (a[i-1] == 't' || a[i-1] == 'T') && + (a[i] == 'e' || a[i] == 'E')) + { + matchlen = 6; + mask |= DELETE; + + } else { + // parse error + throw new IllegalArgumentException( + "invalid permission: " + actions); + } + + // make sure we didn't just match the tail of a word + // like "ackbarfaccept". Also, skip to the comma. + boolean seencomma = false; + while (i >= matchlen && !seencomma) { + switch(a[i-matchlen]) { + case ',': + seencomma = true; + /*FALLTHROUGH*/ + case ' ': case '\r': case '\n': + case '\f': case '\t': + break; + default: + throw new IllegalArgumentException( + "invalid permission: " + actions); + } + i--; + } + + // point i at the location of the comma minus one (or -1). + i -= matchlen; + } + + return mask; + } + + /** + * Return the current action mask. Used by the FilePermissionCollection. + * + * @return the actions mask. + */ + + int getMask() { + return mask; + } + + /** + * Return the canonical string representation of the actions. + * Always returns present actions in the following order: + * read, write, execute, delete. + * + * @return the canonical string representation of the actions. + */ + private static String getActions(int mask) + { + StringBuilder sb = new StringBuilder(); + boolean comma = false; + + if ((mask & READ) == READ) { + comma = true; + sb.append("read"); + } + + if ((mask & WRITE) == WRITE) { + if (comma) sb.append(','); + else comma = true; + sb.append("write"); + } + + if ((mask & EXECUTE) == EXECUTE) { + if (comma) sb.append(','); + else comma = true; + sb.append("execute"); + } + + if ((mask & DELETE) == DELETE) { + if (comma) sb.append(','); + else comma = true; + sb.append("delete"); + } + + return sb.toString(); + } + + /** + * Returns the "canonical string representation" of the actions. + * That is, this method always returns present actions in the following order: + * read, write, execute, delete. For example, if this FilePermission object + * allows both write and read actions, a call to getActions + * will return the string "read,write". + * + * @return the canonical string representation of the actions. + */ + public String getActions() + { + if (actions == null) + actions = getActions(this.mask); + + return actions; + } + + + /** + * Returns a new PermissionCollection object for storing FilePermission + * objects. + *

+ * FilePermission objects must be stored in a manner that allows them + * to be inserted into the collection in any order, but that also enables the + * PermissionCollection implies + * method to be implemented in an efficient (and consistent) manner. + * + *

For example, if you have two FilePermissions: + *

    + *
  1. "/tmp/-", "read" + *
  2. "/tmp/scratch/foo", "write" + *
+ * + *

and you are calling the implies method with the FilePermission: + * + *

+     *   "/tmp/scratch/foo", "read,write",
+     * 
+ * + * then the implies function must + * take into account both the "/tmp/-" and "/tmp/scratch/foo" + * permissions, so the effective permission is "read,write", + * and implies returns true. The "implies" semantics for + * FilePermissions are handled properly by the PermissionCollection object + * returned by this newPermissionCollection method. + * + * @return a new PermissionCollection object suitable for storing + * FilePermissions. + */ + + public PermissionCollection newPermissionCollection() { + return new FilePermissionCollection(); + } + + /** + * WriteObject is called to save the state of the FilePermission + * to a stream. The actions are serialized, and the superclass + * takes care of the name. + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + // Write out the actions. The superclass takes care of the name + // call getActions to make sure actions field is initialized + if (actions == null) + getActions(); + s.defaultWriteObject(); + } + + /** + * readObject is called to restore the state of the FilePermission from + * a stream. + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // Read in the actions, then restore everything else by calling init. + s.defaultReadObject(); + init(getMask(actions)); + } +} + +/** + * A FilePermissionCollection stores a set of FilePermission permissions. + * FilePermission objects + * must be stored in a manner that allows them to be inserted in any + * order, but enable the implies function to evaluate the implies + * method. + * For example, if you have two FilePermissions: + *
    + *
  1. "/tmp/-", "read" + *
  2. "/tmp/scratch/foo", "write" + *
+ * And you are calling the implies function with the FilePermission: + * "/tmp/scratch/foo", "read,write", then the implies function must + * take into account both the /tmp/- and /tmp/scratch/foo + * permissions, so the effective permission is "read,write". + * + * @see java.security.Permission + * @see java.security.Permissions + * @see java.security.PermissionCollection + * + * + * @author Marianne Mueller + * @author Roland Schemers + * + * @serial include + * + */ + +final class FilePermissionCollection extends PermissionCollection +implements Serializable { + + // Not serialized; see serialization section at end of class + private transient List perms; + + /** + * Create an empty FilePermissions object. + * + */ + + public FilePermissionCollection() { + perms = new ArrayList(); + } + + /** + * Adds a permission to the FilePermissions. The key for the hash is + * permission.path. + * + * @param permission the Permission object to add. + * + * @exception IllegalArgumentException - if the permission is not a + * FilePermission + * + * @exception SecurityException - if this FilePermissionCollection object + * has been marked readonly + */ + + public void add(Permission permission) + { + if (! (permission instanceof FilePermission)) + throw new IllegalArgumentException("invalid permission: "+ + permission); + if (isReadOnly()) + throw new SecurityException( + "attempt to add a Permission to a readonly PermissionCollection"); + + synchronized (this) { + perms.add(permission); + } + } + + /** + * Check and see if this set of permissions implies the permissions + * expressed in "permission". + * + * @param p the Permission object to compare + * + * @return true if "permission" is a proper subset of a permission in + * the set, false if not. + */ + + public boolean implies(Permission permission) + { + if (! (permission instanceof FilePermission)) + return false; + + FilePermission fp = (FilePermission) permission; + + int desired = fp.getMask(); + int effective = 0; + int needed = desired; + + synchronized (this) { + int len = perms.size(); + for (int i = 0; i < len; i++) { + FilePermission x = (FilePermission) perms.get(i); + if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(fp)) { + effective |= x.getMask(); + if ((effective & desired) == desired) + return true; + needed = (desired ^ effective); + } + } + } + return false; + } + + /** + * Returns an enumeration of all the FilePermission objects in the + * container. + * + * @return an enumeration of all the FilePermission objects. + */ + + public Enumeration elements() { + // Convert Iterator into Enumeration + synchronized (this) { + return Collections.enumeration(perms); + } + } + + private static final long serialVersionUID = 2202956749081564585L; + + // Need to maintain serialization interoperability with earlier releases, + // which had the serializable field: + // private Vector permissions; + + /** + * @serialField permissions java.util.Vector + * A list of FilePermission objects. + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("permissions", Vector.class), + }; + + /** + * @serialData "permissions" field (a Vector containing the FilePermissions). + */ + /* + * Writes the contents of the perms field out as a Vector for + * serialization compatibility with earlier releases. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + // Don't call out.defaultWriteObject() + + // Write out Vector + Vector permissions = new Vector(perms.size()); + synchronized (this) { + permissions.addAll(perms); + } + + ObjectOutputStream.PutField pfields = out.putFields(); + pfields.put("permissions", permissions); + out.writeFields(); + } + + /* + * Reads in a Vector of FilePermissions and saves them in the perms field. + */ + private void readObject(ObjectInputStream in) throws IOException, + ClassNotFoundException { + // Don't call defaultReadObject() + + // Read in serialized fields + ObjectInputStream.GetField gfields = in.readFields(); + + // Get the one we want + Vector permissions = (Vector)gfields.get("permissions", null); + perms = new ArrayList(permissions.size()); + perms.addAll(permissions); + } +} diff --git a/openjdk/sun/net/dns/ResolverConfigurationImpl.java b/openjdk/sun/net/dns/ResolverConfigurationImpl.java new file mode 100644 index 00000000..7a12796f --- /dev/null +++ b/openjdk/sun/net/dns/ResolverConfigurationImpl.java @@ -0,0 +1,216 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.net.dns; + +import java.util.List; +import java.util.LinkedList; +import java.util.StringTokenizer; +import java.io.IOException; +import cli.System.Net.NetworkInformation.IPAddressCollection; +import cli.System.Net.NetworkInformation.IPInterfaceProperties; +import cli.System.Net.NetworkInformation.NetworkInterface; + +/* + * An implementation of sun.net.ResolverConfiguration for Windows. + */ + +public class ResolverConfigurationImpl + extends ResolverConfiguration +{ + // Lock helds whilst loading configuration or checking + private static Object lock = new Object(); + + // Resolver options + private final Options opts; + + // Addreses have changed + private static boolean changed = false; + + // Time of last refresh. + private static long lastRefresh = -1; + + // Cache timeout (120 seconds) - should be converted into property + // or configured as preference in the future. + private static final int TIMEOUT = 120000; + + // DNS suffix list and name servers populated by native method + private static String os_searchlist; + private static String os_nameservers; + + // Cached lists + private static LinkedList searchlist; + private static LinkedList nameservers; + + // Parse string that consists of token delimited by space or commas + // and return LinkedHashMap + private LinkedList stringToList(String str) { + LinkedList ll = new LinkedList(); + + // comma and space are valid delimites + StringTokenizer st = new StringTokenizer(str, ", "); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (!ll.contains(s)) { + ll.add(s); + } + } + return ll; + } + + // Load DNS configuration from OS + + private void loadConfig() { + assert Thread.holdsLock(lock); + + // if address have changed then DNS probably changed aswell; + // otherwise check if cached settings have expired. + // + if (changed) { + changed = false; + } else { + if (lastRefresh >= 0) { + long currTime = System.currentTimeMillis(); + if ((currTime - lastRefresh) < TIMEOUT) { + return; + } + } + } + + // load DNS configuration, update timestamp, create + // new HashMaps from the loaded configuration + // + loadDNSconfig0(); + + lastRefresh = System.currentTimeMillis(); + searchlist = stringToList(os_searchlist); + nameservers = stringToList(os_nameservers); + os_searchlist = null; // can be GC'ed + os_nameservers = null; + } + + ResolverConfigurationImpl() { + opts = new OptionsImpl(); + } + + public List searchlist() { + synchronized (lock) { + loadConfig(); + + // List is mutable so return a shallow copy + return (List)searchlist.clone(); + } + } + + public List nameservers() { + synchronized (lock) { + loadConfig(); + + // List is mutable so return a shallow copy + return (List)nameservers.clone(); + } + } + + public Options options() { + return opts; + } + + // --- Address Change Listener + + static class AddressChangeListener extends Thread { + public void run() { + for (;;) { + // wait for configuration to change + if (notifyAddrChange0() != 0) + return; + synchronized (lock) { + changed = true; + } + } + } + } + + + // --- Native methods -- + + static void init0() { + } + + static void loadDNSconfig0() { + String searchlist = ""; + String nameservers = ""; + for (NetworkInterface iface : NetworkInterface.GetAllNetworkInterfaces()) { + IPInterfaceProperties props = iface.GetIPProperties(); + IPAddressCollection addresses = props.get_DnsAddresses(); + for (int i = 0; i < addresses.get_Count(); i++) { + cli.System.Net.IPAddress addr = addresses.get_Item(i); + // no IPv6 support + if (addr.get_AddressFamily().Value == cli.System.Net.Sockets.AddressFamily.InterNetwork) { + nameservers = strAppend(nameservers, addr.toString()); + } + } + try { + if (false) throw new cli.System.PlatformNotSupportedException(); + searchlist = strAppend(searchlist, props.get_DnsSuffix()); + } + catch (cli.System.PlatformNotSupportedException _) { + } + } + os_searchlist = searchlist; + os_nameservers = nameservers; + } + + private static String strAppend(String s, String app) { + if (s.equals("")) { + return app; + } + if (app.equals("")) { + return s; + } + return s + " " + app; + } + + static int notifyAddrChange0() { + // TODO we could use System.Net.NetworkInformation.NetworkChange to detect changes + return -1; + } + + static { + java.security.AccessController.doPrivileged( + new sun.security.action.LoadLibraryAction("net")); + init0(); + + // start the address listener thread + AddressChangeListener thr = new AddressChangeListener(); + thr.setDaemon(true); + thr.start(); + } +} + +/** + * Implementation of {@link ResolverConfiguration.Options} + */ +class OptionsImpl extends ResolverConfiguration.Options { +} diff --git a/openjdk/sun/net/www/protocol/file/FileURLConnection.java b/openjdk/sun/net/www/protocol/file/FileURLConnection.java new file mode 100644 index 00000000..4eeca0f2 --- /dev/null +++ b/openjdk/sun/net/www/protocol/file/FileURLConnection.java @@ -0,0 +1,228 @@ +/* + * Copyright 1995-2004 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * Open an file input stream given a URL. + * @author James Gosling + * @author Steven B. Byrne + */ + +package sun.net.www.protocol.file; + +import java.net.URL; +import java.net.FileNameMap; +import java.io.*; +import java.text.Collator; +import java.security.Permission; +import sun.net.*; +import sun.net.www.*; +import java.util.*; +import java.text.SimpleDateFormat; + +import sun.security.action.GetPropertyAction; +import sun.security.action.GetIntegerAction; +import sun.security.action.GetBooleanAction; + +public class FileURLConnection extends URLConnection { + + static String CONTENT_LENGTH = "content-length"; + static String CONTENT_TYPE = "content-type"; + static String TEXT_PLAIN = "text/plain"; + static String LAST_MODIFIED = "last-modified"; + + String contentType; + InputStream is; + + File file; + String filename; + boolean isDirectory = false; + boolean exists = false; + List files; + + long length = -1; + long lastModified = 0; + + protected FileURLConnection(URL u, File file) { + super(u); + this.file = file; + } + + /* + * Note: the semantics of FileURLConnection object is that the + * results of the various URLConnection calls, such as + * getContentType, getInputStream or getContentLength reflect + * whatever was true when connect was called. + */ + public void connect() throws IOException { + if (!connected) { + try { + filename = file.toString(); + isDirectory = file.isDirectory(); + if (isDirectory) { + files = (List) Arrays.asList(file.list()); + } else { + + is = new BufferedInputStream(new FileInputStream(filename)); + + // Check if URL should be metered + boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET"); + if (meteredInput) { + ProgressSource pi = new ProgressSource(url, "GET", (int) file.length()); + is = new MeteredStream(is, pi, (int) file.length()); + } + } + } catch (IOException e) { + throw e; + } + connected = true; + } + } + + private boolean initializedHeaders = false; + + private void initializeHeaders() { + try { + connect(); + exists = file.exists(); + } catch (IOException e) { + } + if (!initializedHeaders || !exists) { + length = file.length(); + lastModified = file.lastModified(); + + if (!isDirectory) { + FileNameMap map = java.net.URLConnection.getFileNameMap(); + contentType = map.getContentTypeFor(filename); + if (contentType != null) { + properties.add(CONTENT_TYPE, contentType); + } + properties.add(CONTENT_LENGTH, String.valueOf(length)); + + /* + * Format the last-modified field into the preferred + * Internet standard - ie: fixed-length subset of that + * defined by RFC 1123 + */ + if (lastModified != 0) { + Date date = new Date(lastModified); + SimpleDateFormat fo = + new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); + fo.setTimeZone(TimeZone.getTimeZone("GMT")); + properties.add(LAST_MODIFIED, fo.format(date)); + } + } else { + properties.add(CONTENT_TYPE, TEXT_PLAIN); + } + initializedHeaders = true; + } + } + + public String getHeaderField(String name) { + initializeHeaders(); + return super.getHeaderField(name); + } + + public String getHeaderField(int n) { + initializeHeaders(); + return super.getHeaderField(n); + } + + public int getContentLength() { + initializeHeaders(); + return (int) length; + } + + public String getHeaderFieldKey(int n) { + initializeHeaders(); + return super.getHeaderFieldKey(n); + } + + public MessageHeader getProperties() { + initializeHeaders(); + return super.getProperties(); + } + + public long getLastModified() { + initializeHeaders(); + return lastModified; + } + + public synchronized InputStream getInputStream() + throws IOException { + + int iconHeight; + int iconWidth; + + connect(); + + if (is == null) { + if (isDirectory) { + FileNameMap map = java.net.URLConnection.getFileNameMap(); + + StringBuffer buf = new StringBuffer(); + + if (files == null) { + throw new FileNotFoundException(filename); + } + + sort(files); + + for (int i = 0 ; i < files.size() ; i++) { + String fileName = (String)files.get(i); + buf.append(fileName); + buf.append("\n"); + } + // Put it into a (default) locale-specific byte-stream. + is = new ByteArrayInputStream(buf.toString().getBytes()); + } else { + throw new FileNotFoundException(filename); + } + } + return is; + } + + // IKVM specific method (sorting moved here to delay java.text.Collator dependency) + private static void sort(List files) { + Collections.sort(files, Collator.getInstance()); + } + + Permission permission; + + /* since getOutputStream isn't supported, only read permission is + * relevant + */ + public Permission getPermission() throws IOException { + if (permission == null) { + String decodedPath = ParseUtil.decode(url.getPath()); + if (File.separatorChar == '/') { + permission = new FilePermission(decodedPath, "read"); + } else { + permission = new FilePermission( + decodedPath.replace('/',File.separatorChar), "read"); + } + } + return permission; + } +}