/* 

                          Firewall Builder

                 Copyright (C) 2002 Vadim Kurland

  Author:  Vadim Kurland     vadim@vk.crocodile.org

  $Id: Compiler.hh,v 1.37 2002/06/03 21:29:32 vkurland Exp $

  This program is free software which we release under the GNU General Public
  License. You may redistribute and/or modify this program under the terms
  of that license as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  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 for more details.
 
  To get a copy of the GNU General Public License, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#ifndef __COMPILER_HH__
#define __COMPILER_HH__

#include <fwbuilder/libfwbuilder-config.h>
#include "fwbuilder/FWException.hh"

#include "fwcompiler/RuleProcessor.hh"

#include <list>
#include <vector>
#include <map>

#include <fstream>
#include <sstream>

namespace libfwbuilder {
    class FWObject;
    class FWOptions;
    class FWObjectDatabase;
    class IPAddress;
    class Address;
    class Service;
    class Network;
    class Firewall;
    class Interface;
    class Rule;
    class RuleSet;
    class PolicyRule;
    class NATRule;
    class RuleElement;
};


namespace fwcompiler {

    class OSConfigurator;

    class FWCompilerException : public libfwbuilder::FWException {
	libfwbuilder::Rule *rule;
	public:
	FWCompilerException(libfwbuilder::Rule *r,const std::string &err);
	libfwbuilder::Rule *getRule() const { return rule; }
    };

    libfwbuilder::Firewall* findFirewallByName(libfwbuilder::FWObjectDatabase *db,
                             const std::string &name) throw(libfwbuilder::FWException);

/* 
 * operations    (see Compiler_ops.cc)
 */

    /**
     * compares rules similarly to cmpstr.
     * returns:
     *
     *   0 if rules are equal, 
     *  -1 if r2 shades r1 
     *   1 if r1 shades r2
     *  -2 if rules do not intersect
     */
    int cmpRules(const libfwbuilder::PolicyRule &r1,const libfwbuilder::PolicyRule &r2);
    int cmpRules(const libfwbuilder::NATRule    &r1,const libfwbuilder::NATRule    &r2);

    /**
     * this operator compares two objects to determine if one of them
     * "shades" another
     */
    bool operator<(const libfwbuilder::Address &o1,const libfwbuilder::Address &o2);
    bool operator<(const libfwbuilder::Service  &o1,const libfwbuilder::Service  &o2);

    /**
     * this operator compares two objects to determine if they are
     * equal
     */
    bool operator==(const libfwbuilder::Address &o1,const libfwbuilder::Address &o2);
    bool operator==(const libfwbuilder::Service  &o1,const libfwbuilder::Service  &o2);

    /**
     * this operator compares two objects to determine if they are
     * equal or one of them "shades" another
     */
    bool operator<=(const libfwbuilder::Address &o1,const libfwbuilder::Address &o2);
    bool operator<=(const libfwbuilder::Service  &o1,const libfwbuilder::Service  &o2);

    /**
     * this method finds intersection of two objects. Objects must
     * be of such types that have address (Host, Firewall,
     * Interface, Network) , otherwise it throws an exception
     *
     * TODO:  implement this as a virtual method of respective classes
     *
     * this method is intended for internal use only
     */
    std::vector<libfwbuilder::FWObject*> 
    _find_obj_intersection(libfwbuilder::Address *o1,
			   libfwbuilder::Address *o2);
    /**
     * this method finds intersection of two services. If one or
     * both objects are not services, it throws exception
     *
     * this method is intended for internal use only
     */
    std::vector<libfwbuilder::FWObject*> 
    _find_srv_intersection(libfwbuilder::Service *o1,
			   libfwbuilder::Service *o2);

    /**
     * this method finds intersection of two ranges of ports
     *
     * this method is intended for internal use only
     */
    bool _find_portrange_intersection(int rs1,int re1,int rs2,int re2,int &rsr,int &rer);
	

    /**
     * this function finds intersection of two atomic rules. Resulting
     * rule may have multiple objects in src,dst and srv, so
     * converting to atomic may be necessary. If rules can not be
     * compared, then it throws an exception. If rules are compatible
     * but have nothing in common, then this method returns rule with
     * empty src,dst,srv. Use isEmpty(Rule &r) to check for this
     * condition
     * 
     * This operator creates and returns new object of class Rule
     * and does not modify r1 and r2
     *
     * This operator works only with interface, src, dst and srv and
     * completely ignores action and other rule options.
     *
     */
    void  getIntersection(const libfwbuilder::PolicyRule &r1,
			  const libfwbuilder::PolicyRule &r2,
			  libfwbuilder::PolicyRule &res);


    /**
     * this function checks if two rules intersect - that is, if there
     * is a non-empty intersection for each rule element. This
     * function does not calculate intersection, it just verifies that
     * it does exsit.
     */
    bool  intersect(const libfwbuilder::PolicyRule &r1,
		    const libfwbuilder::PolicyRule &r2);



    class Compiler {

	void _init(libfwbuilder::FWObjectDatabase *_db,
		   const std::string &fwname);
	void _expand_group_recursive(libfwbuilder::FWObject *o,
				     std::list<libfwbuilder::FWObject*> &ol);

	protected:

	int  _cntr_;
	bool initialized;

        std::list<BasicRuleProcessor*> rule_processors;

        /**
         * add rule processor to the list
         */
        void add(BasicRuleProcessor* rp);

        /**
         *  assembles chain of rule processors and executes it
         */
        void runRuleProcessors();

       /*
	* the following variables are simply a cache for frequently used
	* objects
	*/
	std::map<const std::string,libfwbuilder::Interface*> fw_interfaces;
        std::string                                          fw_id;
        libfwbuilder::FWOptions                             *fwopt;
        std::map<const std::string,libfwbuilder::FWObject*>  objcache;

        /**
         * stores object o and all its children in the cache, recursively
         */
        int cache_objects(libfwbuilder::FWObject *o);

	public:

        /**
         *  rule processor that "injects" rules into the conveyor
         */
        class Begin : public BasicRuleProcessor
        {
            bool init;
            public:
            Begin(const std::string &n);
            virtual bool processNext();
        };

	/**
	 * this processor prints number of rules in the queue on cout
	 * if compiler->verbose is true
	 */
        class printTotalNumberOfRules : public BasicRuleProcessor
        {
            public:
            virtual bool processNext();
        };

	/**
	 * this processor prints rule numbers on cout (trivial
	 * progress indicator)
	 */
        class simplePrintProgress : public BasicRuleProcessor
        {
            std::string current_rule_label;
            public:
            simplePrintProgress() : BasicRuleProcessor("Print progress") {};
            virtual bool processNext();
        };

	/**
	 * generic rule debugger: prints name of the previous rule
	 * processor in a chain and then a rule if its number is
	 * compiler->debug_rule.  Uses virtual method
	 * Compiler::debugPrintRule to actually print the rule
	 */
        class Debug : public BasicRuleProcessor
        {
            public:
            virtual bool processNext();
        };

        /**
         * prints rule in some universal format (close to that visible
         * to user in the GUI). Used for debugging purposes
         */
        virtual std::string debugPrintRule(libfwbuilder::Rule *rule);

        /**
         * returns pointer to cached interface
         */
        libfwbuilder::Interface* getCachedFwInterface(const std::string &id)
        { return fw_interfaces[id]; }

        /**
         * returns cached firewall object ID
         */
        std::string getFwId() { return fw_id; }

        /**
         * returns pointer to the cached firewall options object
         */
        libfwbuilder::FWOptions* getCachedFwOpt() { return fwopt; }
        
        /**
         *  stores object with given ID in the cache
         */
        void cacheObj(libfwbuilder::FWObject *o);

        /**
         * does cache lookup for object with given ID
         */
        libfwbuilder::FWObject* getCachedObj(const std::string &id)
        {
            return objcache[id];
        }
	
	/**
	 * internal: scans children of 's' and, if finds host or
	 * firewall with multiple interfaces, replaces reference to
	 * that host or firewall with a set of references to its
	 * interfaces. Argument 's' should be a pointer at either src
	 * or dst in the rule
	 */
	void _expandAddr(libfwbuilder::Rule *rule,libfwbuilder::FWObject *s);

	/**
	 * internal: like _expandAddr, but expands address range
	 * objects
	 */
	void _expandAddressRanges(libfwbuilder::Rule *rule,libfwbuilder::FWObject *s);

	/*
	 * normalizes port range (makes sure that niether range start
	 * nor end is <0 and so on
	 */
	void normalizePortRange(int &rs,int &re);


        /**
         * This method returns true if one of the following conditions is met:
         *
         * 1. obj1 is the same as obj2 (compares ID of both objects), or 
         * 2. obj1 is a child of obj2 on any depth, or
         * 3. address of obj1 matches that of any obj2's interfaces, or 
         * 4. address of obj1 is a broadcast address of one of 
         *    the interfaces of obj2
         * 5. address of obj1 is a broadcast (255.255.255.255)
         */
        bool complexMatch(libfwbuilder::Address *obj1,libfwbuilder::Address *obj2);

        /**
         * This method finds interface of obj2 (which is usually
         * firewall object, but not necessarily so) which is connected
         * to the subnet on which obj1 is located.
         */
        libfwbuilder::Interface* findInterfaceFor(libfwbuilder::Address *obj1,libfwbuilder::Address *obj2);
        

        /**
         * prints error message and aborts the program
         */
	void abort(const std::string &errstr);

        /**
         * prints an error message and returns
         */
	void error(const std::string &warnstr);

        /**
         * prints warning message
         */
	void warning(const std::string &warnstr);

	int                                debug;
	int                                debug_rule;
	bool                               verbose;

	fwcompiler::OSConfigurator        *osconfigurator;
	libfwbuilder::FWObjectDatabase    *dbcopy;
	libfwbuilder::Firewall            *fw;

	libfwbuilder::RuleSet             *combined_ruleset;
	libfwbuilder::RuleSet             *temp_ruleset;

	std::ostringstream                 output;


	virtual ~Compiler();

	Compiler(libfwbuilder::FWObjectDatabase *_db,
		 const std::string &fwname);

	Compiler(libfwbuilder::FWObjectDatabase *_db,
		 const std::string &fwname,
		 fwcompiler::OSConfigurator *_oscnf);

	void setDebugLevel(int dl) { debug=dl;       }
	void setDebugRule(int dr)  { debug_rule=dr;  }
	void setVerbose(bool v)    { verbose=v;      }


	std::string getCompiledScript() { return output.str(); output.str(""); }


	void expandGroupsInRuleElement(libfwbuilder::RuleElement *s);


        /**
	 * this method should return platform name. It is used 
	 * to construct proper error and warning messages.
	 */
	virtual std::string myPlatformName() =0;

	std::string getUniqueRuleLabel();

	virtual std::string createRuleLabel(const std::string &interface_,
				       int rule_num);

	virtual std::string createRuleLabel(const std::string &txt,
				       const std::string &interface_,
				       int rule_num);

	/**
	 * prolog should pack rules into combined_ruleset and return
	 * number of rules found
	 */
	virtual int  prolog();
	virtual void compile();
	virtual void epilog();

	/**
	 * prints rule marked for debugging (its number * is in
	 * debug_rule member variable)
	 */
        void debugRule();

    };


}

#endif
