package org.tanukisoftware.wrapper.security;

/*
 * Copyright (c) 1999, 2014 Tanuki Software, Ltd.
 * http://www.tanukisoftware.com
 * All rights reserved.
 *
 * This software is the proprietary information of Tanuki Software.
 * You shall use it only in accordance with the terms of the
 * license agreement you entered into with Tanuki Software.
 * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html
 */

import java.security.AccessControlException;
import java.security.BasicPermission;
import java.security.Permission;
import java.util.ArrayList;
import java.util.StringTokenizer;

import org.tanukisoftware.wrapper.WrapperManager;

/**
 * WrapperEventPermissions are used to grant the right to register to start
 * receiving events from the Wrapper.
 * <p>
 * Some of these permissions can result in performance degredations if used
 * impropperly.
 * <p>
 * The following are examples of how to specify the permission within a policy
 * file.
 * 
 * <pre>
 *   grant codeBase "file:../lib/-" {
 *     // Grant various permissions to a specific service.
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "service";
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "service, core";
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "*";
 *   };
 * </pre>
 * <p>
 * Possible eventTypes include the following:
 * <table border='1' cellpadding='2' cellspacing='0'>
 * <tr>
 * <th>Permission Event Type Name</th>
 * <th>What the Permission Allows</th>
 * <th>Risks of Allowing this Permission</th>
 * </tr>
 * 
 * <tr>
 * <td>service</td>
 * <td>Register to obtain events whenever the Wrapper service receives any
 * service events.</td>
 * <td>Malicious code could receive this event and never return and thus cause
 * performance and timeout problems with the Wrapper. Normal use of these events
 * are quite safe however.</td>
 * </tr>
 * 
 * <tr>
 * <td>control</td>
 * <td>Register to obtain events whenever the Wrapper receives any system
 * control signals.</td>
 * <td>Malicious code could trap and consome control events, thus preventing an
 * application from being shut down cleanly.</td>
 * </tr>
 * 
 * <tr>
 * <td>core</td>
 * <td>Register to obtain events on the core workings of the Wrapper.</td>
 * <td>Malicious code or even well meaning code can greatly affect the
 * performance of the Wrapper simply by handling these methods slowly. Some of
 * these events are fired from within the core timing code of the Wrapper. They
 * are useful for testing and performance checks, but in general they should not
 * be used by most applications.</td>
 * </tr>
 * </table>
 * 
 * @author Leif Mortenson <leif@tanukisoftware.com>
 */


public final class WrapperUserEventPermission extends BasicPermission
{
    /**
     * Serial Version UID.
     */
    private static final long serialVersionUID = 8916489326587298168L;
    private final int EVENT_MIN = 1;
    private final int EVENT_MAX = 32767;
    private ArrayList m_eventArr;

    /*---------------------------------------------------------------
     * Constructors
     *-------------------------------------------------------------*/
    /**
     * Creates a new WrapperEventPermission for the specified service.
     * 
     * @param action The event type or event types to be registered.
     */
    public WrapperUserEventPermission( String action )
    {
        super( "fireUserEvent", String.valueOf( action ) );
        parseValids( action );
    }
    
    /**
     * Creates a new WrapperEventPermission for the specified service.
     *
     * @param name Name of the event.
     * @param action The event type or event types to be registered.
     */
    public WrapperUserEventPermission( String name, String action )
    {
        super( name, action );
        parseValids( action );
    }

    /**
     * Return the canonical string representation of the eventTypes.
     *  Always returns present eventTypes in the following order: 
     *  start, stop, pause, continue, interrogate. userCode.
     *
     * @return The canonical string representation of the eventTypes.
     */
    public String getActions()
    {
        String s = "";
        for ( int i = 0; i < m_eventArr.size(); i++ )
        {
            if ( i > 0 )
            {
                s = s.concat( "," );
            }
            s = s.concat( (String)m_eventArr.get( i ) );
        }
        return s;
    }

    /**
     * Checks if this WrapperEventPermission object "implies" the
     *  specified permission.
     * <P>
     * More specifically, this method returns true if:<p>
     * <ul>
     *  <li><i>p2</i> is an instanceof FilePermission,<p>
     *  <li><i>p2</i>'s eventTypes are a proper subset of this object's eventTypes,
     *      and<p>
     *  <li><i>p2</i>'s service name is implied by this object's service name.
     *      For example, "MyApp*" implies "MyApp".
     * </ul>
     *
     * @param p2 the permission to check against.
     *
     * @return True if the specified permission is implied by this object.
     */
    public boolean implies( Permission p2 )
    {
        int current, min, max, check, border;
        String element;
        
        check = Integer.parseInt( p2.getActions() );
        for ( int i = 0; i < m_eventArr.size(); i++ )
        {
            element = (String)m_eventArr.get( i );
            border = element.indexOf( '-' );
            if ( border >= 0 )
            {
                min = Integer.parseInt( element.substring( 0, border ) );
                max = Integer.parseInt( element.substring( border + 1 ) );
                if ( min <= check && check <= max )
                {
                    return true;
                }
            }
            else
            {
                current = Integer.parseInt( element );
                if ( current == check )
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * This method evaluates the passed in permission's action String and stores them in 
     * chunks in an array
     *
     * @param action the permission's actions
     */
    private void parseValids( String action )
    {
        int lastValue = 0, currentValue;
        m_eventArr = new ArrayList();
        if ( action.compareTo( "*" ) == 0 )
        {
            m_eventArr.add( new String( EVENT_MIN + "-" + EVENT_MAX ) );
            return;
        }
        StringTokenizer strok = new StringTokenizer( action.trim(), "," );
        while ( strok.hasMoreTokens() )
        {
            String element = strok.nextToken();
            if ( element.indexOf( '*' ) >= 0 )
            {
                throw new AccessControlException( WrapperManager.getRes().getString( "can''t define ''*'' inside a sequence." ) );
            }
            int range = element.indexOf( "-" );
            if ( range >= 0 )
            {
                if ( range == 0 )
                {
                    if ( m_eventArr.size() != 0 )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} has to be first element in sequence.", element ) );
                    }
                    else
                    {
                        lastValue = Integer.parseInt( element.substring( 1 ) );
                        if ( lastValue <= EVENT_MIN || lastValue > EVENT_MAX )
                        {
                            throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) );
                        }
                        m_eventArr.add( new String( EVENT_MIN + "-" + lastValue ) );
                    }
                }
                else if ( range == element.length() - 1 )
                {
                    currentValue = Integer.parseInt( element.substring( 0, element.length() - 1 ) );
                    if ( currentValue <= EVENT_MIN || currentValue > EVENT_MAX )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) );
                    }
                    if ( currentValue < lastValue )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) );
                    }
                    lastValue = currentValue;
                    if ( strok.hasMoreTokens() )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} has to be last element in sequence.", element ) );
                    }
                    m_eventArr.add( currentValue + "-" + EVENT_MAX );
                }
                else
                {
                    currentValue = Integer.parseInt( element.substring( 0, range ) );
                    if ( currentValue <= EVENT_MIN || currentValue > EVENT_MAX )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) );
                    }
                    if ( currentValue < lastValue )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) );
                    }
                    lastValue = currentValue;
                    currentValue = Integer.parseInt( element.substring( range + 1 ) );
                    if ( currentValue <= EVENT_MIN || currentValue > EVENT_MAX )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) );
                    }
                    if ( currentValue < lastValue )
                    {
                        throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) );
                    }
                    m_eventArr.add( lastValue + "-" + currentValue );
                    lastValue = currentValue;
                }
            }
            else
            {
                currentValue = Integer.parseInt( element );
                if ( currentValue < lastValue )
                {
                    throw new java.security.AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) );
                }
                lastValue = currentValue;
                m_eventArr.add( element );
            }
        }
    }
}
