/*
 *   
 *
 * Copyright  1990-2006 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 com.sun.midp.security;

import javax.microedition.io.*;

import javax.microedition.lcdui.*;

import com.sun.midp.lcdui.*;

import com.sun.midp.midlet.*;

import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;

import com.sun.midp.events.EventQueue;

import com.sun.midp.io.j2me.storage.*;

import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;

/**
 * Contains methods to handle with the various security state information of a
 * a MIDlet suite.
 */
public final class SecurityHandler {

    /** The security token for this class. */
    private static SecurityToken classSecurityToken;

    /**
     * Creates a security domain with a list of permitted actions or no list
     * to indicate all actions. The caller must be have permission for
     * <code>Permissions.MIDP</code> or be the first caller of
     * the method for this instance of the VM.
     *
     * @param ApiPermissions for the token
     * @param domain name of the security domain
     *
     * @exception SecurityException if caller is not permitted to call this
     *            method
     */
    public SecurityHandler(byte[] ApiPermissions, String domain) {
    	// No-op since we don't cache any permissions in Java
    	// Query native security manager every time
    }

    /**
     * Creates a security domain with a list of permitted actions or no list
     * to indicate all actions. The caller must be have permission for
     * <code>Permissions.MIDP</code> or be the first caller of
     * the method for this instance of the VM.
     *
     * @param securityToken security token of the caller
     * @param ApiPermissions for the token, can be null
     * @param domain name of the security domain
     *
     * @exception SecurityException if caller is not permitted to call this
     *            method
     */
    public SecurityHandler(SecurityToken securityToken,
            byte[] ApiPermissions, String domain) {
    	// Do not cache anything
    	// Ask native security manager everytime
    }

    /**
     * Get the status of the specified permission.
     * If no API on the device defines the specific permission
     * requested then it must be reported as denied.
     * If the status of the permission is not known because it might
     * require a user interaction then it should be reported as unknown.
     *
     * @param permission to check if denied, allowed, or unknown.
     * @return 0 if the permission is denied; 1 if the permission is allowed;
     *  -1 if the status is unknown
     */
    public int checkPermission(String permission) {
        boolean found = false;
        int i;

        for (i = 0; i < Permissions.NUMBER_OF_PERMISSIONS; i++) {
        	if (Permissions.getName(i).equals(permission)) {
        		found = true;
        		break;
        	}
        }
        
        if (found) {
        	MIDletSuite current =
		MIDletStateHandler.getMidletStateHandler().getMIDletSuite();

        	if (current != null) {
        		// query native security mgr for status
		    return checkPermissionStatus0(current.getID(), permission);
        	}
        }
        
        return 0; // Deny permission
    }

    /**
     * Check for permission and throw an exception if not allowed.
     * May block to ask the user a question.
     * <p>
     * The title, and question strings will be translated,
     * if a string resource is available.
     * Since the strings can have substitution token in them, if there is a
     * "%" it must changed to "%%". If a string has a %1, the app parameter
     * will be substituted for it. If a string has a "%2, the resource
     * parameter will be substituted for it. If a string has a %3, the
     * extraValue parameter will be substituted for it.
     *
     * @param permission ID of the permission to check for,
     *      the ID must be from
     *      {@link com.sun.midp.security.Permissions}
     * @param title Resource constant for the title of the dialog
     * @param question Resource constant for the question to ask user
     * @param app name of the application to insert into a string
     *        can be null if no %1 a string
     * @param resource string to insert into a string,
     *        can be null if no %2 in a string
     * @param extraValue string to insert into a string,
     *        can be null if no %3 in a string
     *
     * @return true if the permission was allow and was not allowed
     *    before
     *
     * @exception SecurityException if the permission is not
     *            allowed by this token
     * @exception InterruptedException if another thread interrupts the
     *   calling thread while this method is waiting to preempt the
     *   display.
     */
    public boolean checkForPermission(int permission, int title, int question,
        int oneshotQuestion, String app, String resource, String extraValue)
        throws InterruptedException {

        return checkForPermission(permission, title, question,
            oneshotQuestion, app, resource, extraValue,
            SecurityToken.STD_EX_MSG);
    }


    /**
     * Check for permission and throw an exception if not allowed.
     * May block to ask the user a question.
     * <p>
     * The title, question, and answer strings will be translated,
     * if a string resource is available.
     * Since the strings can have substitution token in them, if there is a
     * "%" it must changed to "%%". If a string has a %1, the app parameter
     * will be substituted for it. If a string has a "%2, the resource
     * parameter will be substituted for it. If a string has a %3, the
     * extraValue parameter will be substituted for it.
     *
     * @param permission ID of the permission to check for,
     *      the ID must be from
     *      {@link com.sun.midp.security.Permissions}
     * @param title Resource constant for the title of the dialog
     * @param question Resource constant for the question to ask user
     * @param oneShotQuestion Resource constant for the oneshot question to
     *                        ask the user
     * @param app name of the application to insert into a string
     *        can be null if no %1 a string
     * @param resource string to insert into a string,
     *        can be null if no %2 in a string
     * @param extraValue string to insert into a string,
     *        can be null if no %3 in a string
     * @param exceptionMsg message if a security exception is thrown
     *
     * @return <code>true</code> if the permission was allowed and was
     * not allowed before; <code>false</code>, if permission is granted..
     *
     * @exception SecurityException if the permission is not
     *            allowed by this token
     * @exception InterruptedException if another thread interrupts the
     *   calling thread while this method is waiting to preempt the
     *   display.
     */
    public boolean checkForPermission(int permission, int title, int question,
        int oneShotQuestion, String app, String resource, String extraValue,
        String exceptionMsg) throws InterruptedException {
    	
    	MIDletSuite current =
            MIDletStateHandler.getMidletStateHandler().getMIDletSuite();

        if (current == null) {
	    // Deny. Internal suite should not call this function
	    return true;
        } else {
	    return !checkPermission0(current.getID(),
				     Permissions.getName(permission));
        }
    }

    /**
     * Ask the user yes/no permission question.
     *
     * @param token security token with the permission to preempt the
     *        foreground display
     * @param title Resource constant for the title of the dialog
     * @param question Resource constant for the question to ask user
     * @param oneShotQuestion Resource constant for the oneshot question to
     *                        ask the user
     * @param app name of the application to insert into a string
     *        can be null if no %1 a string
     * @param resource string to insert into a string,
     *        can be null if no %2 in a string
     * @param extraValue string to insert into a string,
     *        can be null if no %3 in a string
     *
     * @return true if the user says yes else false
     *
     * @exception InterruptedException if another thread interrupts the
     *   calling thread while this method is waiting to preempt the
     *   display.
     */
    public static boolean askUserForPermission(SecurityToken token,
            int title, int question, String app, String resource,
            String extraValue) throws InterruptedException {
    	// Allow Push interrupt since the decision is already made
	// at native Push level
        return true;
    }

    /**
     * Initializes the security token for this class, so it can
     * perform actions that a normal MIDlet Suite cannot.
     *
     * @param token security token for this class.
     */
    static void initSecurityToken(SecurityToken token) {
        if (classSecurityToken != null) {
            return;
        }

        classSecurityToken = token;
    }
    
    /**
     * Query native security manager for permission.
     * This call may block if user needs to be asked.
     *
     * @param suiteId the MIDlet suite the permission should be checked against
     * @param permission the permission id
     * 
     * @return true if permission is granted. Otherwise, false.
     */
    private native boolean checkPermission0(String suiteId, String permission);
    
    /**
     * Get the status of the specified permission.
     * This is to implement public API MIDlet.checkPermission()
     * and will not block calling thread.
     * 
     * If no API on the device defines the specific permission
     * requested then it must be reported as denied.
     * If the status of the permission is not known because it might
     * require a user interaction then it should be reported as unknown.
     *
     * @param suiteId the MIDlet suite the permission should be checked against
     * @param permission to check if denied, allowed, or unknown.
     * @return 0 if the permission is denied; 1 if the permission is allowed;
     *  -1 if the status is unknown
     */
    private native int checkPermissionStatus0(String suiteId,
					      String permission);
}
