/************************************************
* Columbia University CS Department
* Network Security Lab
*
* Open and Survivable Embedded Systems (OASES)
*
* Structured Policy Command Language (SPCL)
*
*
* This software is provided as is without any
* warranty or guarantee of merchantibility
* or fitness for a particular purpose. This
* software is released under the terms of the
* GNU General Public License (GPL).
*
* Last updated: May 9, 2003
*
***********************************************/

package spcl.policyengine;

import spcl.data.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.util.regex.*;
import java.text.*; //DateFormat

/**
 * <p>
 * The PolicyEngine is the core of execution of a
 * policy. A general policy is intially loaded and resolved and
 * then a sub-policy is computed based on input events and
 * returned to the querying system.
 * </p>
 * <p>
 * The model of computation is very Prolog-like. We first narrow the
 * search tree by considering the possible namespace (zone), policy,
 * principal, object, and group we are talking about. Then we match
 * the rules that apply and determine if there are any conflicts.
 * </p>
 * <p>
 * This class is a concrete implementation of a machine that
 * understands the execution of policy as defined by SPCL.
 * </p>
 * <p>
 * This class wraps a serversocket and is a runnable single-threaded
 *  server, a PolicyLoader, and a ClassLoader.
 * </p>
 * <br><br>
 * The model of execution is thus:
 *  <ul>
 *   <li> there is a period of data initialization
 *    of the primary data structures in this machine</li>
 *
 *   <li> These data structures are the 'map' of the relations
 *    between policies, principales, zones, objects, and groups.</li>
 *
 *   <li>looped interpretation and updating of rule state based on input</li>
 *
 *   <li>possible loading of new data structures, followed by a resolution
 *    phase</li>
 *  </ul>
 *
 * @author locasto@cs.columbia.edu
 *
 *
*/
public class PolicyEngine extends ClassLoader implements Runnable, PolicyLoader{

	private Hashtable policyFiles = new Hashtable();

	private Hashtable zones=new Hashtable();

	/** no Zone can be named zone in the source code, so naming our
	 * default Zone 'zone:zone' protects it.
	*/
	private Zone defaultZone = new Zone( "zone:zone" );

	private PolicyEngineConsole console;
	private Thread listener;
	private boolean stop = false;
	private ServerSocket server = null;
	private Socket client = null;
	private int port = -1;

	private StringBuffer sb = new StringBuffer( 1024 );

	/* these variables are maintained by the policyengine's server thread */

	/** This object represents the current zone, policy to search
	 * first, and system object we are currently dealing with 
	 */
	private PolicyContext current = new PolicyContext();

	/* these are vars used on startup for getting different config files, etc */

	public static String SPCL_HOME = System.getProperty( "spcl.home" );
	private static String configFileName = SPCL_HOME+File.separatorChar+"conf"+File.separatorChar+"pole.properties";
	private static String policyInstallerClassName;


	/** Create a policy engine instance */
	public PolicyEngine( ClassLoader parent ) throws Exception{
	    super( parent );

        if( SPCL_HOME == null || SPCL_HOME.equals( "" ) )
          throw new Exception( "SPCL_HOME not defined." );

        defaultZone.attach( new Policy( "defaultPolicy" ) );
        zones.put( defaultZone.getName(), defaultZone );

        //install supplied 'bootstrap' policy given on command line
        install( policyInstallerClassName );

        console = new PolicyEngineConsole();

        configure();

        init();

        System.out.println( "\nPolicyEngine bound to :"+port );
    }

	/** 
	 * Read the class file and create a PolicyInstaller 
	 * subclass instance. Then, call the doInstall() method.
	 *
	*/
	private void install( String classname ) throws PolicyException, IOException, Exception{

		String filename = "";

		filename = classname.replace( '.', File.separatorChar );
		filename+=".class";
		filename = SPCL_HOME+File.separatorChar+"conf"
                              +File.separatorChar+"policy"
                              +File.separatorChar+filename;

		policyFiles.put( classname, pull( new File( filename ) ) );

		Object o = this.loadClass( classname, true ).newInstance();

		PolicyInstaller installer = ((PolicyInstaller)o);
		installer.setPolicyLoader( ((PolicyLoader)this) );
		installer.doInstall();
		System.out.println( "PolicyEngine::install( "+classname+" )" );
		
		//save on memory
		policyFiles.remove( classname );
	}

	private byte [] pull( File f ) throws IOException{

		String name=f.getName();
		int totalFileBytes = (int)f.length();
		byte [] b = new byte[ totalFileBytes ];

		FileInputStream fin = new FileInputStream( f );

		int i=fin.read( b );
		fin.close();
		return b;
	}

	/**
	 * open the config file and get our startup information:
	 *   spcl.port
	 *   spcl.console.port
	 *   spcl.shutdownmessage
	 *
	 * additional info we can put in here is a list of all legal
	 * policyinstaller files to bootstrap with (for the future)
	 *
	*/
	private void configure() throws Exception{

		Properties configs = new Properties();

		configs.load( new FileInputStream( new File( configFileName ) ) );

		port = Integer.parseInt( configs.getProperty( "spcl.port", "3780" ) );
		console.setPolicyEngine( this );
		console.setPort( Integer.parseInt( configs.getProperty( "spcl.console.port", "3781" ) ) );
		console.setMessage( configs.getProperty( "spcl.shutdownmessage", "" ) );
		configs = null;

	}

	public void init() throws Exception{

		server = new ServerSocket( port, 10, InetAddress.getLocalHost() );
		listener = new Thread( this );
		listener.start();
		
		console.init();

	}

	public void run(){

		PolicyRequest request=null;
		PolicyResponse response=null;

		ObjectInputStream oin=null;
		ObjectOutputStream oout=null;

		while( !stop ){
			listener.yield();
			request = new PolicyRequest();

			try{
				
				client = server.accept();
				System.out.println( "PolicyEngine got client: "+client.getInetAddress() );

				oout = new ObjectOutputStream( client.getOutputStream() );
				oout.flush();
				oin = new ObjectInputStream( client.getInputStream() );

				try{
					while( !request.getContent().equals("bye") ){
						request = ((PolicyRequest)oin.readObject());

						response = process( request );

						oout.writeObject( response );
						oout.flush();
					}
				}catch( ClassCastException ccex ){
					System.err.println( "Wrong input type: "+ccex.getMessage() );
				}catch( NullPointerException npex ){
					System.err.println( "Null pointer: "+npex.getMessage() );
					npex.printStackTrace();
				}catch( Exception ex ){
				  //maybe class cast
				  System.err.println( "PolicyEngine::run() problem getting input:"+ex.getMessage() );
				  ex.printStackTrace();
				}
				oin.close();
				oout.close();
				client.close();
				listener.yield();
			}catch( Exception e ){
				e.printStackTrace();
				System.err.println( "PolicyEngine::run() problem running server:"+e.getMessage() );
			}finally{
			  oin=null;
			  oout=null;
			  client=null;
			}
		}
		System.exit( 0 );
	}

	private PolicyResponse process( PolicyRequest request ){
		
		PolicyResponse response = new PolicyResponse( request );
		String content = request.getContent();
		content = content.trim();
		sb.setLength(0);
		Enumeration e=null;

		if( content.equals( "hello" ) ){
			response.setContent( "<hi/>" );
		}else if( content.equals( "bye" ) ){
			response.setContent( "<bye/>" );
		}else if( content.equals( "list principals" ) ){
			e = current.policy.getPrincipals();
			sb.append("<principals>");
			while( e.hasMoreElements() ){
				sb.append( "\n\t<principal name=\"" );
				sb.append( ((Principal)e.nextElement()).getName() );
				sb.append( "\"/>" );
			}
			sb.append("\n</princpals>");
			response.setContent( sb.toString() );
		}else if( content.equals( "list objects" ) ){
			e = current.policy.getObjects();
			sb.append("<objects>");
			while( e.hasMoreElements() ){
				sb.append( "\n\t<object name=\"" );
				sb.append( ((SystemObject)e.nextElement()).getName() );
				sb.append( "\"/>" );
			}
			sb.append("\n</objects>");
			response.setContent( sb.toString() );
		}else if( content.equals( "list groups" ) ){
			e = current.policy.getGroups();
			sb.append("<groups>");
			while( e.hasMoreElements() ){
				sb.append( "\n\t<group name=\"" );
				sb.append( ((Group)e.nextElement()).getName() );
				sb.append( "\"/>" );
			}
			sb.append("\n</groups>");
			response.setContent( sb.toString() );
		}else if( content.equals( "list policies" ) ){
			e = current.zone.getPolicies();
			sb.append("<policies>");
			while( e.hasMoreElements() ){
				sb.append( "\n\t<policy name=\"" );
				sb.append( ((Policy)e.nextElement()).getName() );
				sb.append( "\"/>" );
			}
			sb.append("\n</policies>");
			response.setContent( sb.toString() );
		}else if( content.equals( "list active rules" ) ){
			//for all principals and groups in this policy, list all active rules
		}else if( content.equals( "list suspended rules" ) ){
			//for all principals and groups in this policy, list suspended rules
		}else if( content.equals( "list rules" ) ){
			//for all principals and groups in this policy, list all rules
		}else if( content.equals( "current" ) || content.equals( "context" ) ){
			//print out current context
			sb.append( current.toString() );
			response.setContent( sb.toString() );
		}else if( content.startsWith( "switch policy " ) ){
			int lastSpacePos = content.lastIndexOf( " " );
			if( lastSpacePos==-1 ){
				response.setContent( "<no reason=\"bad format\"/>" );
			}else{
				String to_policy = content.substring( lastSpacePos+1 );
				if( switchPolicy( to_policy ) ){
					response.setContent( "<new-policy name=\""+to_policy+"\"/>" );
				}else{
					response.setContent( "<no reason=\"named policy dne\"/>" );
				}
			}
		}else if( content.startsWith( "switch zone " ) ){
			//only if current object is in new zone!!
			int lastSpacePos = content.lastIndexOf( " " );
			if( lastSpacePos==-1 ){
				response.setContent( "<no reason=\"bad format\"/>" );
			}else{
				String to_zone = content.substring( lastSpacePos+1 );
				if( switchZone( to_zone ) ){
					response.setContent( "<new-zone name=\""+to_zone+"\"/>" );
				}else{
					response.setContent( "<no reason=\"named policy dne\"/>" );
				}
			}
		}else if( content.startsWith( "switch object " ) ){
			int lastSpacePos = content.lastIndexOf( " " );
			if( lastSpacePos==-1 ){
				response.setContent( "<no reason=\"bad format\"/>" );
			}else{
				String to_object = content.substring( lastSpacePos+1 );
				if( switchObject( to_object ) ){
					response.setContent( "<new-object name=\""+to_object+"\"/>" );
				}else{
					response.setContent( "<no reason=\"named object dne\"/>" );
				}
			}
		}else if( content.equals( "list zones" ) ){
			sb.append("<zonelist>");
			e = zones.elements();
			while( e.hasMoreElements() ){
				sb.append( "\n\t<zone name=\"" );
				sb.append( ((Zone)e.nextElement()).getName() );
				sb.append( "\"/>" );
			}
			sb.append("\n</zonelist>");
			response.setContent( sb.toString() );
		}else if( content.startsWith( "CAN" ) || content.startsWith( "can" ) ){
			//evaulate the "CAN" query
			System.err.println( "\t>> checking query ["+content+"]" );
			sb.append( doQuery( content ) );
			response.setContent( sb.toString() );
		}else{
			response.setContent( "<no/>" );
		}

		return ((PolicyResponse)response);
	}

	/**
	 * use the policy engine to answer questions like:
	 * can michael do "open" with (fridge.door=locked)?
	   can [principal] do [action] with (param1=x AND param2=y);
	   can, michael, do, action, with, param1, x, AND, param2, y
	   args[0]=CAN
	   args[1]=principalName;
	   args[2]=do
	   args[3]=actionString
	   current.policy
	   current.object
	   current.zone
	*/
	private String doQuery( String content ){
		if( current.zone==null || current.zone.isEmpty() ||
		    current.policy==null || current.object==null )
			return "<no/>";
		Principal dude = null; //current client principal
		Group dudeGroup=null;
		String response = "<no/>";
		Vector paramList = new Vector(); //store new Variable( param1, x );
		Variable newVar = null;

		StringTokenizer cmdTokens = new StringTokenizer( content, " \n\t\r(),;?\"", false );
		String [] args = new String[cmdTokens.countTokens()]; //load cmdTokens into args
		int i=0;
		if( args.length<4 ){
			System.err.println( "\t>> args < 4" );
			return "<no/>";
		}
		
		System.err.print( "\t>> " );
		while( cmdTokens.hasMoreTokens() ){
			args[i]=cmdTokens.nextToken();
			System.err.print( " ["+args[i]+"] " );
			i++;
		}
		System.err.print( "\n" );
		if( !args[2].equalsIgnoreCase( "do" ) )
			return "<no/>";
		if( (args.length>=5) && !args[4].equalsIgnoreCase( "with" ) ){
			System.err.println( "\t>> missing \'with\'" );
			return "<no/>";
		}
		String principalName = args[1];
		String actionString = args[3];
		String objectName = current.object.getName();

		//parse content
		//consume tokens from args
		//AND is a delimiter
		//x=5
		//y=true (express bindings)
		// we should get this pattern:  id eq val AND id eq val
		// string EQ QUOTE string QUOTE AND string EQ number AND string EQ TRUE | FALSE ...
		// create new variable, guess its type: ''==string true or false == boolean, else NUMBER
		i=5; //args[5]
		String newVarName="";
		int newVarType=-1;
		String newVarValue="";
		while( i<args.length ){

			newVarName = consumeString( args[i] );
			if( newVarName==null ){ 
				System.err.println( "\t>> parseConditions::bad var name" );
				return "<no/>";
			}
			i++;
			if( i<args.length ){
				if( !consumeEquals( args[i] ) ){ 
					System.err.println( "\t>> parseConditions::expected =" );
					return "<no/>";
				}
				i++;
			}else{
				System.err.println( "\t>> parseConditions::expected =, saw EOS" );
				return "<no/>";
			}
			if( i<args.length ){
				newVarValue = consumeString( args[i] );
				if( newVarValue==null ){ 
					System.err.println( "\t>> parseConditions::bad value" );
					return "<no/>";
				}
				i++;
			}else{
				System.err.println( "\t>> parseConditions::expected value, saw EOS" );
				return "<no/>";
			}
			if( i<args.length ){
				if( !consumeAND( args[i] ) ) {
					System.err.println( "\t>> parseConditions::expected AND" );
					return "<no/>";
				}
				i++;
			}else{
				//no error, there are just no more clauses
			}
			//decide type
			newVarValue = newVarValue.trim();
			if( newVarValue.equalsIgnoreCase( "true" ) ||
			    newVarValue.equalsIgnoreCase( "false" ) ){
				newVarType = spcl.data.Variable.BOOLEAN;
			}else if( newVarValue.startsWith( "\'" ) &&
			    newVarValue.endsWith("\'") ){
				newVarType = spcl.data.Variable.STRING;
				newVarValue = newVarValue.replace( '\'', '\0' );
			}else{
				try{
					Integer.parseInt( newVarValue );
					newVarType = spcl.data.Variable.NUMBER;
				}catch( NumberFormatException nfex ){
					return "<no/>";
				}
			}
			newVar = new Variable( newVarName, newVarType, newVarValue );
			paramList.addElement( newVar );
		}

		//get principal, object, action, param list from current Policy
		//find the principal in our current policy
		if( !current.policy.hasPrincipal( principalName ) ){
			System.err.println( "\t>> no named principal in policy" );
			return "<no/>";
		}else{
			dude = current.policy.getPrincipal( principalName );
		}

		//do principal resolution
		response = checkRules( dude.getRules(), (new int[dude.getNumRules()]),
		                       actionString, paramList );
		if( response.equals( "<allow/>" ) || response.equals( "<deny/>" ) ){
			return response;
		}else if( response.equals( "<wishy-washy/>" ) ){
			//fall through
			System.err.println( "\t>> principal def was wishy-washy" );
		}else{
			//dont' know, error, return no
			System.err.println( "\t>> principal def returned unknown answer" );
			return "<no/>";
		}
		//if principal exists, but no rule applied (all wishy washy), find
		//all groups with him a member of them and look at their rules
		Vector userGroups = current.policy.getUserGroups( principalName );
		System.err.println( "\t>> current.policy.getUserGroups("+principalName+");" );
		//for each group, checkRules( group.getRules(), ...);
		//do group resolution...it is permissive, first one to make a decision
		//wins...
		System.err.println( "\t>> usergroups size="+userGroups.size() );
		for( i=0;i<userGroups.size();i++ ){
			dudeGroup = (Group)userGroups.elementAt(i);
			response = checkRules( dudeGroup.getRules(), (new int[dudeGroup.getNumRules()]),
		                       actionString, paramList );
			if( response.equals( "<allow/>" ) || response.equals( "<deny/>" ) ){
				return response;
			}else if( response.equals( "<wishy-washy/>" ) ){
				//fall through
				System.err.println( "\t>> group def was wishy-washy" );
			}else{
				System.err.println( "\t>> group def had unknown response" );
				return "<no/>";
			}
		}

		//finally, check default_p
		Principal defP = current.policy.getDefaultPrincipal();
		response = checkRules( defP.getRules(), (new int[defP.getNumRules()]),
		                       actionString, paramList );
		if( response.equals( "<allow/>" ) || response.equals( "<deny/>" ) ){
			return response;
		}else if( response.equals( "<wishy-washy/>" ) ){
			//default response
			System.err.println( "\t>> default principal says no (wishy-washy)" );
			return "<no/>";
		}else{
			System.err.println( "\t>> default principal says no" );
			return "<no/>";
		}
	}

	private
	String checkRules( Enumeration rules, int [] responses,
	                   String actionString, Vector paramList ){
		int i=0;
		Rule tempRule=null;
		if( rules==null || responses.length==0 || paramList==null )
			return "<wishy-washy/>";
		//ok, now we have enough info. we've found the appropriate ruleset/group/principal
		//we look through the rules that are active and match params from paramList
		//if a rule matches, we getAccess() on it into responses
		while( rules.hasMoreElements() && i<responses.length ){
			tempRule = (Rule)rules.nextElement();
			if( tempRule.isActive() ){
				responses[i]=tempRule.evaluate( paramList, actionString, current.object );
				if( responses[i]==spcl.data.Rule.ALLOW ||
					responses[i]==spcl.data.Rule.DENY ){
					System.err.println( "\t\t>> tempRule["+tempRule.getName()+"].fire() " );
					tempRule.fire();
				}
			}else{
				responses[i]=spcl.data.Rule.WISHY_WASHY;
			}
			System.err.println( "\t\t\t>> responses["+tempRule.getName()+"]="+responses[i] );
			i++;
		}
		//responses[all]==spcl.data.Rule.WISHY_WASHY, fall through
		boolean foundAllow=false;
		boolean foundDeny=false;
		boolean foundWishyWashy=false;
		for( i=0;i<responses.length;i++ ){
			switch( responses[i] ){
				case spcl.data.Rule.DENY:
					if( foundDeny ){
						//double deny?? do nothing...
					}else if( foundAllow ){
						//inconsistent
						return "<inconsistent-policy-deny/>";
					}else if( foundWishyWashy ){
						//let it fly
					}
					foundDeny=true;
					break;
				case spcl.data.Rule.ALLOW:
					if( foundAllow ){
						//double allow?? do nothing...
					}else if( foundDeny ){
						//inconsistent
						return "<inconsistent-policy-deny/>";
					}else if( foundWishyWashy ){
						//let it fly
					}
					foundAllow=true;
					break;
				case spcl.data.Rule.WISHY_WASHY:
					if( foundDeny ){
						//wishy washy and deny
					}else if( foundAllow ){
						//wishy washy and allow
					}else if( foundWishyWashy ){
						//let it fly
					}
					foundWishyWashy=true;
					break;
			}
		}
		if( foundDeny ){
			return "<deny/>";
		}else if( foundAllow ){
			return "<allow/>";
		}
		return "<wishy-washy/>";
	}

	private boolean consumeAND( String arg ){
		arg=arg.trim();
		return (arg.equalsIgnoreCase( "and" ));
	}

	private boolean consumeEquals( String arg ){
		arg=arg.trim();
		return (arg.equals( "=" ));
	}
	
	private String consumeString( String arg ){
		if( consumeAND(arg) || consumeEquals(arg) )
			return null;
		else
			return (arg.trim());
	}

	/** Explicit zone context switch. Also occurs implicitely after a load() */
	public boolean switchZone( String newZone ){
		Zone currZone = current.zone;
		current.zone = lookupZone( newZone );
		if( current.zone==null ){
			current.zone=currZone;
			return false;
		}
		current.policy = current.zone.getFirst();
		current.object = null;
		return true;
	}

	public boolean switchPolicy( String newPolicy ){
		Policy currPol = current.policy;
		current.policy = current.zone.getPolicy( newPolicy );
		if( current.policy==null ){
			current.policy = currPol;
			return false;
		}
		current.object = null;
		return true;
	}

	public boolean switchObject( String to_objectName ){
		if( current.policy.hasObject( to_objectName ) ){
			current.object = current.policy.getObject( to_objectName );
			return true;
		}else{
			return false;
		}
	}

	void shutDown(){
		try{
			stop = true;
			if( server!=null )
				server.close();
		}catch( Exception ex ){
			System.out.println( "PolicyEngine::shutDown() problem: "+ex.getMessage() );
		}
		System.exit( 0 );
	}

//----------------------------------- ClassLoader methods

	protected Class findClass( String classname ) throws ClassNotFoundException{

		Class c=null;
		ClassLoader myParent = this.getClass().getClassLoader();
		if( myParent == null )
			myParent = ClassLoader.getSystemClassLoader();

		if( null == c ){

			//should try to say "parent, find class X" to prevent
			//malicious programs from redefining java.lang.String for
			//example
			c = super.findClass( classname );

			byte [] b = loadClassData( classname );

			c = defineClass( classname, b, 0, b.length );

			if( null == c )
				throw new ClassNotFoundException( classname );
		}

		resolveClass( c );

		return c;
	}

	/**
	 * locate the appropriate bytes and return them
	*/
	private byte [] loadClassData( String classname ){
		byte [] b = new byte[0];
		b = ((byte[])policyFiles.get( classname ));
		if( b==null || b.length==0 ){
			return new byte[0];
		}else{
			return ((byte[])b);
		}
	}



//----------------------------------- interface PolicyLoader

	public void load( Zone zone ){
		//install the zone in this policyengine
		zones.put( zone.getName(), zone );
		current.zone = zone;
		current.policy = zone.getFirst();
	}

	/**
	 * Assumes that zone already exists.
	 * @throws PolicyException - if zone DNE
	*/
	public void load( Policy policy, String zoneName ) throws PolicyException{
		Zone z = lookupZone( zoneName );
		if( z==null )
			throw new PolicyException(
				"Could not load policy "+policy.getName()+". Zone "+zoneName+" does not exist.",
				PolicyException.NULL_ZONE
			);
		z.attach( policy );
		current.zone = z;
		current.policy = z.getFirst();
	}

	/** Look up the current zone. Does not auto-switch zone contexts. */
	public Zone lookupZone( String zoneName ){
		if( defaultZone.getName().equals( zoneName ) ){
			return defaultZone;
		}else{
			return ((Zone)zones.get( zoneName ));
		}
	}

	public boolean unload( String policyName ){
		return false;
	}

	public boolean suspend( String policyName ){
		return false;
	}

	public boolean resume( String policyName ){
		return false;
	}

	//for special case 'system' and 'time' and boolean, no meaning
	public
	boolean resolveCondition( int sys_var, String relop, boolean rval ){
		return false;
	}

	public
	boolean resolveCondition( int sys_var, String relop, String rval ){

		if( sys_var!=spcl.data.PolicyLoader.SYSTEM_TIME )
			return false;

		Date dnow = new java.util.Date();
		long now = -1;

		DateFormat df = DateFormat.getDateInstance( DateFormat.SHORT );
		DateFormat dtf = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT );
		DateFormat tf = DateFormat.getTimeInstance( DateFormat.SHORT );

		Date rvalDate = null;
		long rvaltime = 0;

		try{
			rvalDate = df.parse( rval );
			dnow = df.parse( df.format( dnow ) );
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::full date" );
		}catch( Exception dfex ){
			System.err.println( "\t>> full date failed:"+dfex.getMessage() );
			try{
				rvalDate = dtf.parse( rval );
				dnow = dtf.parse( dtf.format( dnow ) );
				System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::full date full time" );
			}catch( Exception dtfex ){
				System.err.println( "\t>> full date+time failed:"+dtfex.getMessage() );
				try{
					rvalDate = tf.parse( rval );
					dnow = tf.parse( tf.format( dnow ) );
					System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::time only" );
				}catch( Exception tfex ){
					System.err.println( "\t>> full time failed:"+tfex.getMessage() );
					System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::no parse time" );
					return false;
				}
			}
		}
		
		now = dnow.getTime();

		if( relop.equals( "==" ) ){
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::dates are equ" );
			return ((dnow.equals( rvalDate )));
		}else if( relop.equals( "!=" ) ){
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::dates are !equ" );
			return ((!dnow.equals( rvalDate )));
		}else if( relop.equals( "<" ) ){
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::now < rval" );
			return ((dnow.before( rvalDate )));
		}else if( relop.equals( ">" ) ){
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::now > rval" );
			return ((dnow.after( rvalDate )));
		}else if( relop.equals( "<=" ) ){
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::now <= rval" );
			return ((dnow.before( rvalDate ))||((dnow.equals( rvalDate ))));
		}else if( relop.equals( ">=" ) ){
			System.err.println( "\t>> resolveCondition( SYSTEM_TIME )::now >= rval" );
			return ((dnow.after( rvalDate ))||((dnow.equals( rvalDate ))));
		}else{
			return false;
		}
	}

	//for special case boolean
	public 
	boolean resolveCondition( Variable var, String relop, boolean rval ){
		if( var.getType()!=spcl.data.Variable.BOOLEAN )
			return false;

		if( relop.equals( "==" ) ){
			return (var.getBooleanValue()==rval);
		}else if( relop.equals( "!=" ) ){
			return (var.getBooleanValue()!=rval);
		}else{
			return false;
		}
	}

	//rc( ip_addr,">", "rval", spcl.data.Variable.STRING );
	public
	boolean resolveCondition( Variable var, String relop, String rval ){
		int i_rval;

		switch( var.getType() ){

			case spcl.data.Variable.NUMBER:
				i_rval = Integer.parseInt( rval );
				if( relop.equals( "==" ) ){
					return ((i_rval==var.getNumberValue()));
				}else if( relop.equals( "!=" ) ){
					return ((i_rval!=var.getNumberValue()));
				}else if( relop.equals( ">" ) ){
					return ((var.getNumberValue()>i_rval));
				}else if( relop.equals( "<" ) ){
					return ((var.getNumberValue()<i_rval));
				}else if( relop.equals( "<=" ) ){
					return ((var.getNumberValue()<=i_rval));
				}else if( relop.equals( ">=" ) ){
					return ((var.getNumberValue()>=i_rval));
				}else{
					return false;
				}
			case spcl.data.Variable.BOOLEAN:
				return false;
			case spcl.data.Variable.STRING:
				if( relop.equals( "==" ) ){
					return ((rval.equals( var.getStringValue())));
				}else if( relop.equals( "!=" ) ){
					return ((!rval.equals( var.getStringValue())));
				}else if( relop.equals( ">" ) ){
					return false;
				}else if( relop.equals( "<" ) ){
					return false;
				}else if( relop.equals( "<=" ) ){
					return false;
				}else if( relop.equals( ">=" ) ){
					return false;
				}else{
					return false;
				}
			default:
		}
		return false;
	}



//----------------------------------- private static methods

	private static void doUsage(){

		System.out.println( "\n\nUsage:\n\tjava spcl.PolicyEngine -stop\n" );
		System.out.println( "\tjava spcl.PolicyEngine -policy [fully.qualified.classname]\n" );
		System.out.println( "The -policy file parameter is relative to SPCL_HOME/conf/policy/" );
	}

	public static void execute( String [] args ){
	    if( args.length==2 && args[0].equals( "-policy" ) ){
			policyInstallerClassName = args[1];
		}else if( args.length==1 && args[0].equals( "-stop" ) ){
			try{
				new StopTask( configFileName );
			}catch( Exception e ){
				System.err.println( "Problem running stoptask: "+e.getMessage() );
			}
			System.exit( 0 );
		}else{
		  doUsage();
		  System.exit( -2 );
		}
	}

}

