/* * * * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program 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. * * This program 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 at /legal/license.txt). * * 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 or visit www.sun.com if you need additional * information or have any questions. */ package javax.microedition.io; import java.security.Permission; import java.security.PermissionCollection; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; /* * An abstract class that is the superclass of all permissions used with the * Generic Connection Framework. A GCFPermission consists of a URI string * representing access rights to a connection based on the specified protocol scheme. * * A specification that extends the Generic Connection Framework with a new protocol scheme * should define a subclass of GCFPermission to guard access to the protocol. */ public abstract class GCFPermission extends Permission { private URIParser parser; // A trick to create an URI parser and normalize the URI before passing it // to the superclass constructor: we use kind of thread-local storage // for URI parser. It works unless the superclass constructor somehow // triggers construction of one more subclass instance. private static final Hashtable map = new Hashtable(); private static String normalize(String uri, PortRangeNormalizer portRangeNormalizer, PathNormalizer pathNormalizer, boolean normalizeAuthority) { URIParser p = new URIParser(uri, portRangeNormalizer, pathNormalizer, normalizeAuthority); Thread t = Thread.currentThread(); // NOTE: the mapping can be non-empty at this point if the previous // ctor invocation was terminated abruptly with an exception thrown // from superclass ctor. We override the existing mapping. map.put(t, p); return p.getURI(); } private static URIParser getParser() { Thread t = Thread.currentThread(); Object o = map.remove(t); return (URIParser)o; } /** * Constructs a <code>GCFPermission</code> with the specified URI. * The URI must begin with a string indicating the protocol * scheme, followed by a ':'. * * @param uri the URI string. * * @throws IllegalArgumentException if <code>uri</code> is malformed. * @throws NullPointerException if <code>uri</code> is <code>null</code>. * * @see #getURI */ public GCFPermission(String uri) { this(uri, false, null, null, false); } GCFPermission(String uri, boolean requireAuthority) { this(uri, requireAuthority, null, null, false); } GCFPermission(String uri, boolean requireAuthority, PortRangeNormalizer portRangeNormalizer) { this(uri, requireAuthority, portRangeNormalizer, null, false); } GCFPermission(String uri, boolean requireAuthority, PortRangeNormalizer portRangeNormalizer, PathNormalizer pathNormalizer) { this(uri, requireAuthority, portRangeNormalizer, pathNormalizer, false); } GCFPermission(String uri, boolean requireAuthority, PortRangeNormalizer portRangeNormalizer, PathNormalizer pathNormalizer, boolean normalizeAuthority) { super(normalize(uri, portRangeNormalizer, pathNormalizer, normalizeAuthority)); parser = getParser(); String scheme = parser.getScheme(); if (scheme == null || "".equals(scheme)) { throw new IllegalArgumentException("Expected protocol scheme: " + uri); } if (requireAuthority && !getURI().startsWith(scheme + "://")) { throw new IllegalArgumentException("Invalid URI: " + uri); } } /** * Returns the URI of this GCFPermission. * * @return the URI string, identical to Permission.getName(). */ public String getURI() { return getName(); } /** * Returns the protocol scheme of this GCFPermission. * The protocol scheme is the string preceding the first ':' in the URI. * * @return the protocol scheme portion of the URI string. */ public String getProtocol() { return parser.getScheme(); } final int getMinPort() { return parser.getPortRange()[0]; } final int getMaxPort() { return parser.getPortRange()[1]; } final String getHost() { return parser.getHost(); } final String getPath() { return parser.getPath(); } final String getSchemeSpecificPart() { return parser.getSchemeSpecificPart(); } final boolean impliesByHost(GCFPermission that) { String thisHost = this.parser.getHost(); String thatHost = that.parser.getHost(); boolean equal = thisHost.equals(thatHost); if (equal) { return true; } // Handle empty host names - server connections if ("".equals(thisHost) || "".equals(thatHost)) { return equal; } if (thisHost.startsWith("*")) { return thatHost.endsWith(thisHost.substring(1)); } return false; } final boolean impliesByPorts(GCFPermission that) { return (this.getMinPort() <= that.getMinPort()) && (this.getMaxPort() >= that.getMaxPort()); } final void checkNoHostPort() { parser.checkNoHost(); parser.checkNoPortRange(); } final void checkHostPortPathOnly() { parser.checkNoFragment(); parser.checkNoUserInfo(); parser.checkNoQuery(); } final void checkHostPortOnly() { parser.checkNoFragment(); parser.checkNoUserInfo(); parser.checkNoPath(); parser.checkNoQuery(); } final void checkPortRange() { parser.checkPortRange(); } final void checkNoPortRange() { parser.checkNoPortRange(); } } /** * A GCFPermissionCollection stores a collection * of GCF permissions. GCFPermission 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 in an efficient (and consistent) manner. * * Subclasses should override <code>add</code> method to * * @see java.security.Permission */ final class GCFPermissionCollection extends PermissionCollection { private static final Class gcfPermissionClass; private final Class permissionClass; private final Vector permissions = new Vector(6); static { try { gcfPermissionClass = Class.forName("javax.microedition.io.GCFPermission"); } catch (ClassNotFoundException e) { throw new Error(e.toString()); } } /** * Create an empty GCFPermissionCollection object. * */ public GCFPermissionCollection(Class clazz) { if (!gcfPermissionClass.isAssignableFrom(clazz)) { throw new IllegalArgumentException(); } permissionClass = clazz; } /** * Adds a permission to the collection. * * @param permission the Permission object to add. * * @exception IllegalArgumentException - if the permission is not a * GCFPermission, or if * the permission is not of the * same Class as the other * permissions in this collection. * * @exception SecurityException - if this GCFPermissionCollection object * has been marked readonly */ public void add(Permission permission) { if (!permissionClass.isInstance(permission)) { throw new IllegalArgumentException( "Invalid permission class: " + permission); } if (isReadOnly()) { throw new SecurityException( "Cannot add a Permission to a readonly PermissionCollection"); } permissions.addElement(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 (!permissionClass.isInstance(permission)) { return false; } GCFPermission perm = (GCFPermission) permission; int perm_low = perm.getMinPort(); int perm_high = perm.getMaxPort(); Enumeration search = permissions.elements(); int count = permissions.size(); int port_low[] = new int[count]; int port_high[] = new int[count]; int port_range_count = 0; while (search.hasMoreElements()) { GCFPermission cur_perm = (GCFPermission)search.nextElement(); if (cur_perm.impliesByHost(perm)) { if (cur_perm.impliesByPorts(perm)) { return true; } port_low[port_range_count] = cur_perm.getMinPort(); port_high[port_range_count] = cur_perm.getMaxPort(); port_range_count++; } } // now we need to determine if found port ranges cover perm port range. // we will sort the port ranges by the low border and will try to found // the longest continues range covered by these ranges // we will use simple x^2 sort here, cause it is rare situation when we // have many port ranges. for (int i = 0; i < port_range_count; i++) { for (int j = 0; j < port_range_count - 1; j++) { if (port_low[j] > port_low[j+1]) { int tmp = port_low[j]; port_low[j] = port_low[j+1]; port_low[j+1] = tmp; tmp = port_high[j]; port_high[j] = port_high[j+1]; port_high[j+1] = tmp; } } } int current_low = port_low[0]; int current_high = port_high[0]; for (int i = 1; i < port_range_count; i++) { if (port_low[i] > current_high + 1) { //end of continious range if (current_low <= perm_low && current_high >= perm_high) { return true; } if (perm_low <= current_high) { return false; } current_low = port_low[i]; current_high = port_high[i]; } else { if (current_high < port_high[i]) { current_high = port_high[i]; } } } return (current_low <= perm_low && current_high >= perm_high); } /** * Returns an enumeration of all the GCFPermission objects in the * container. * * @return an enumeration of all the GCFPermission objects. */ public Enumeration elements() { return permissions.elements(); } }