/*
 * $Id$
 *
 * Copyright 1996-2008 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 com.sun.javatest;

import java.io.FileDescriptor;
import java.net.InetAddress;

import java.lang.RuntimePermission;
import java.security.Permission;
import java.util.PropertyPermission;

/**
 * This class is set for JT Harness running as an application.  Currently, it imposes
 * almost no security restrictions at all: its existence prevents anyone else
 * (e.g. a test running in this JVM) from setting a more restrictive security manager.
 *
 * Although not required for running under JDK1.0.2, extra definitions for forward
 * compatability with JDK1.1 are also provided. They will effectively be ignored
 * by JDK1.0.2.
 */

public class JavaTestSecurityManager extends SecurityManager
{
    /**
     * Try to install a copy of this security manager. If another security manager is
     * already installed, the install will fail;  a warning message wil, be written to
     * the console if the previously installed security manager is not a subtype of
     * com.sun.javatest.JavaTestSecurityManager.
     * The install can be suppressed by setting the system property
     *  "javatest.security.noSecurityManager" to true.
     */
    public static void install() {
        try {
            // install our own permissive security manager, to prevent anyone else
            // installing a less permissive one.
            final String noSecurityMgr = "javatest.security.noSecurityManager";
            if (Boolean.getBoolean(noSecurityMgr)) {
                System.err.println();
                System.err.println("     ---- WARNING -----");
                System.err.println();
                System.err.println("JT Harness did not install its own Security Manager");
                System.err.println("because the property " + noSecurityMgr + " was set.");
                System.err.println("This is not a fatal error, but it may affect the");
                System.err.println("execution of sameJVM tests");
                System.err.println();
            }
            else {
                try {
                    // test to see if permission API available:
                    // if it's not, we'll get an exception and load
                    // an old-style security manager
                    Class.forName("java.security.Permission");
                    System.setSecurityManager(new NewJavaTestSecurityManager());
                }
                catch (ClassNotFoundException e) {
                    System.setSecurityManager(new JavaTestSecurityManager());
                }
            }
        }
        catch (SecurityException e) {
            SecurityManager sm = System.getSecurityManager();
            if (!(sm instanceof JavaTestSecurityManager)) {
                System.err.println();
                System.err.println("     ---- WARNING -----");
                System.err.println();
                System.err.println("JT Harness could not install its own Security Manager");
                System.err.println("because of the following exception:");
                System.err.println("     " + e);
                System.err.println("This is not a fatal error, but it may affect the");
                System.err.println("execution of sameJVM tests");
                System.err.println();
            }
        }
    }

    // These are the JDK1.0.2 security methods
    public void checkAccept(String host, int port) { }
    public void checkAccess(Thread g) { }
    public void checkAccess(ThreadGroup g) { }
    public void checkConnect(String host, int port) { }
    public void checkConnect(String host, int port, Object context) { }
    public void checkCreateClassLoader() { }
    public void checkDelete(String file) { }
    public void checkExec(String cmd) { }

    // tests which call System.exit() should not cause JT Harness to exit
    public void checkExit(int status) {
        if (allowExit == false) {
            if (verbose) {
                System.err.println(getClass().getName() + ": System.exit() forbidden");
                new Throwable().printStackTrace();
            }
            throw new SecurityException("System.exit() forbidden by JT Harness");
        }
    }

    public void checkLink(String lib) { }
    public void checkListen(int port) { }
    public void checkPackageAccess(String pkg) { }
    public void checkPackageDefinition(String pkg) { }

    // allowing tests to get at and manipulate the system properties
    // is too dangerous to permit when multiple tests are running,
    // possibly simultaneously, in the same JVM.
    public synchronized void checkPropertiesAccess() {
        if (allowPropertiesAccess == false) {
            if (verbose) {
                System.err.println(getClass().getName() + ": properties access forbidden");
                new Throwable().printStackTrace();
            }
            throw new SecurityException("Action forbidden by JT Harness: checkPropertiesAccess");
        }
    }

    public void checkPropertyAccess(String key) { }
    public void checkRead(FileDescriptor fd) { }
    public void checkRead(String file) { }
    public void checkRead(String file, Object context) { }
    public void checkSetFactory() { }
    public boolean checkTopLevelWindow(Object window) { return true; }
    public void checkWrite(FileDescriptor fd) { }
    public void checkWrite(String file) { }

    // These methods are added for forward-compatability with JDK1.1
    public void checkAwtEventQueueAccess() { }
    public void checkMemberAccess(Class clazz, int which) { }
    public void checkMulticast(InetAddress maddr) { }
    public void checkMulticast(InetAddress maddr, byte ttl) { }
    public void checkPrintJobAccess() { }
    public void checkSecurityAccess(String provider) { }
    public void checkSystemClipboardAccess() { }

    /**
     * Set whether or not the JVM may be exited. The default value is "false".
     * @param bool true if the JVM may be exited, and false otherwise
     * @return the previous value of this setting
     */
    public boolean setAllowExit(boolean bool) {
        boolean prev = allowExit;
        allowExit = bool;
        return prev;
    }

    /**
     * Set whether or not the set of system properties may be accessed.
     * The default value is determioned by the system property
     * "javatest.security.allowPropertiesAccess".
     * @param bool true if the system properties may be accessed, and false otherwise
     * @return the previous value of this setting
     */
    public boolean setAllowPropertiesAccess(boolean bool) {
        boolean prev = allowPropertiesAccess;
        allowPropertiesAccess = bool;
        return prev;
    }

    static private boolean allowExit = false; // no overrides on this one; API control only
    static private boolean allowPropertiesAccess =
        Boolean.getBoolean("javatest.security.allowPropertiesAccess");
    static private boolean verbose =
        Boolean.getBoolean("javatest.security.verbose");
}

class NewJavaTestSecurityManager extends JavaTestSecurityManager
{
    public void checkPermission(Permission perm) {
        // allow most stuff, but limit as appropriate
        if (perm instanceof RuntimePermission) {
            if (perm.getName().equals("exitVM"))
                checkExit(0);
            if (perm.getName().equals("createSecurityManager")) {
                super.checkPermission(new java.lang.RuntimePermission("createSecurityManager"));
            }
        }
        else if (perm instanceof PropertyPermission) {
            if (((PropertyPermission)(perm)).getActions().equals("read,write"))
                checkPropertiesAccess();
        }
    }
}
