#!/bin/sh
#
#     Shorewall Packet Filtering Firewall Control Program - V4.2
#
#     This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
#
#     (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 - Tom Eastep (teastep@shorewall.net)
#
#	This file should be placed in /sbin/shorewall6.
#
#	Shorewall documentation is available at http://www.shorewall.net
#
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of Version 2 of the GNU General Public License
#	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 for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#	If an error occurs while starting or restarting the firewall, the
#	firewall is automatically stopped.
#
#	The firewall uses configuration files in /etc/shorewall/ - skeleton
#	files are included with the firewall.
#
#	Commands are:
#
#          shorewall6 add <iface>[:<host>] zone    Adds a host or subnet to a zone
#          shorewall6 delete <iface>[:<host>] zone Deletes a host or subnet from a zone
#          shorewall6 dump                         Dumps all Shorewall6-related information
#                                                  for problem analysis
#	   shorewall6 start 			   Starts the firewall
#	   shorewall6 restart			   Restarts the firewall
#	   shorewall6 stop			   Stops the firewall
#	   shorewall6 status			   Displays firewall status
#	   shorewall6 reset			   Resets iptables packet and
#						   byte counts
#	   shorewall6 clear			   Open the floodgates by
#						   removing all iptables rules
#						   and setting the three permanent
#						   chain policies to ACCEPT
#	   shorewall6 refresh			   Rebuild the common chain to
#						   compensate for a change of
#						   broadcast address on any "detect"
#						   interface.
#	   shorewall6 [re]load [ <directory> ] <system>
#						   Compile a script and install it on a
#						   remote Shorewall6 Lite system.
#	   shorewall6 show <chain> [ <chain> ... ] Display the rules in each <chain> listed
#          shorewall6 show actions                 Displays the available actions
#	   shorewall6 show log			   Print the last 20 log messages
#	   shorewall6 show connections		   Show the kernel's connection
#						   tracking table
#	   shorewall6 show nat			   Display the rules in the nat table
#	   shorewall6 show {mangle|tos}		   Display the rules in the mangle table
#	   shorewall6 show tc			   Display traffic control info
#	   shorewall6 show classifiers		   Display classifiers
#          shorewall6 show capabilities            Display iptables/kernel capabilities
#          shorewall6 show vardir                  Display the VARDIR setting.
#	   shorewall6 version			   Display the installed version id
#	   shorewall6 check [ -e ] [ <directory> ] Dry-run compilation.
#	   shorewall6 try <directory> [ <timeout> ] Try a new configuration and if
#						   it doesn't work, revert to the
#						   standard one. If a timeout is supplied
#						   the command reverts back to the
#						   standard configuration after that many
#						   seconds have elapsed after successfully
#						   starting the new configuration.
#	   shorewall6 logwatch [ refresh-interval ] Monitor the local log for Shorewall6
#						    messages.
#	   shorewall6 drop <address> ...	   Temporarily drop all packets from the
#						   listed address(es)
#	   shorewall6 reject <address> ...	   Temporarily reject all packets from the
#						   listed address(es)
#	   shorewall6 allow <address> ...	   Reenable address(es) previously
#						   disabled with "drop" or "reject"
#	   shorewall6 save [ <file> ]		   Save the list of "rejected" and
#						   "dropped" addresses so that it will
#						   be automatically reinstated the
#						   next time that Shorewall6 starts.
#                                                  Save the current state so that 'shorewall6
#                                                  restore' can be used.
#
#          shorewall6 forget [ <file> ]            Discard the data saved by 'shorewall6 save'
#
#          shorewall6 restore [ <file> ]           Restore the state of the firewall from
#                                                  previously saved information.
#
#          shorewall6 ipaddr { <address>/<cidr> | <address> <netmask> }
#
#                                                  Displays information about the network
#                                                  defined by the argument[s]
#
#          shorewall6 iprange <address>-<address>  Decomposes a range of IP addresses into
#                                                  a list of network/host addresses.
#
#          shorewall6 ipdecimal { <address> | <integer> }
#
#                                                  Displays the decimal equivalent of an IP
#                                                  address and vice versa.
#
#          shorewall6 safe-start [ <directory> ]   Starts the firewall and promtp for a c
#                                                  confirmation to accept or reject the new
#                                                  configuration
#
#          shorewall6 safe-restart [ <directory> ] Restarts the firewall and prompt for a
#                                                  confirmation to accept or reject the new
#                                                  configuration
#
#          shorewall6 compile [ -e ] [ <directory> ] <filename>
#                                                  Compile a firewall program file.

#
# Set the configuration variables from shorewall6.conf
#
#     $1 = Yes: read the params file
#     $2 = Yes: check for STARTUP_ENABLED
#     $3 = Yes: Check for LOGFILE
#     
#
get_config() {

    ensure_config_path

    if [ "$1" = Yes ]; then
	params=$(find_file params)

	if [ -f $params ]; then
	    . $params
	fi
    fi

    config=$(find_file shorewall6.conf)
    
    if [ -f $config ]; then
	if [ -r $config ]; then
	    . $config
	else
	    echo "Cannot read $config! (Hint: Are you root?)" >&2
	    exit 1
	fi
    else
	echo "$config does not exist!" >&2
	exit 2
    fi

    ensure_config_path
    
    if [ -z "$EXPORT" -a "$(id -u)" = 0 ]; then
	#
	# This block is avoided for compile for export and when the user isn't root
	#
	export CONFIG_PATH

	if [ "$3" = Yes ]; then
	    [ -z "$LOGFILE" ] && LOGFILE=/var/log/messages

	    if [ -n "$(syslog_circular_buffer)" ]; then
		LOGREAD="logread | tac"
	    elif [ -f $LOGFILE ]; then
		LOGREAD="tac $LOGFILE"
	    else
		echo "LOGFILE ($LOGFILE) does not exist!" >&2
		exit 2
	    fi
	fi

	if [ -n "$IPTABLES" ]; then
	    if [ ! -x "$IPTABLES" ]; then
		echo "   ERROR: The program specified in IPTABLES does not exist or is not executable" >&2
		exit 2
	    fi
	else
	    IPTABLES=$(mywhich iptables 2> /dev/null)
	    if [ -z "$IPTABLES" ] ; then
		echo "   ERROR: Can't find iptables executable" >&2
		exit 2
	    fi
	fi

	export IPTABLES

	#
	# Compile by non-root needs no restore file
	#
	[ -n "$RESTOREFILE" ] || RESTOREFILE=restore

	validate_restorefile RESTOREFILE

	export RESTOREFILE

	if [ "$2" = Yes ]; then
	    case $STARTUP_ENABLED in
		No|no|NO)
		    echo "   ERROR: Shorewall6 startup is disabled. To enable startup, set STARTUP_ENABLED=Yes in ${CONFDIR}/shorewall6.conf" >&2
		    exit 2
		    ;;
		Yes|yes|YES)
		    ;;
		*)
		    if [ -n "$STARTUP_ENABLED" ]; then
			echo "   ERROR: Invalid Value for STARTUP_ENABLE: $STARTUP_ENABLED" >&2
			exit 2
		    fi
		    ;;
	    esac
	fi

	case ${TC_ENABLED:=Internal} in
	    No|NO|no)
	        TC_ENABLED=
		;;
	esac

	[ -n "LOGFORMAT" ] && LOGFORMAT="${LOGFORMAT%%%*}"

	[ -n "$LOGFORMAT" ] || LOGFORMAT="Shorewall6:"

	export LOGFORMAT

	if [ -n "$STARTUP_LOG" ]; then
	    if [ -n "$LOG_VERBOSITY" ]; then
		case $LOG_VERBOSITY in
		    -1)
			;;
		    0|1|2)
			;;
		    *)
		        echo "  ERROR: Invalid LOG_VERBOSITY ($LOG_VERBOSITY)" >&2
			exit 2;
			;;
		esac
	    else
		LOG_VERBOSITY=2;
	    fi
	else
	    LOG_VERBOSITY=-1;
	fi

    else
	STARTUP_LOG=
	LOG_VERBOSITY=-1
    fi   

    if [ -n "$SHOREWALL_SHELL" ]; then
	if [ ! -x "$SHOREWALL_SHELL" ]; then
	    echo "   WARNING: The program specified in SHOREWALL6_SHELL does not exist or is not executable; falling back to /bin/sh" >&2
	    SHOREWALL_SHELL=/bin/sh
	fi
    fi

    case $VERBOSITY in
	-1|0|1|2)
	    ;;
	*)
	    if [ -n "$VERBOSITY" ]; then
		echo "   ERROR: Invalid VERBOSITY setting ($VERBOSITY)" >&2
		exit 2
	    else
		VERBOSITY=2
	    fi
	    ;;
    esac

    [ -n "$USE_VERBOSITY" ] && VERBOSE=$USE_VERBOSITY || VERBOSE=$(($VERBOSE_OFFSET + $VERBOSITY))

    if [ $VERBOSE -lt -1 ]; then
	VERBOSE=-1
    elif [ $VERBOSE -gt 2 ]; then
	VERBOSE=2
    fi

    export VERBOSE

    [ -n "${HOSTNAME:=$(hostname)}" ]

    [ -n "$RSH_COMMAND" ] || RSH_COMMAND='ssh ${root}@${system} ${command}'
    [ -n "$RCP_COMMAND" ] || RCP_COMMAND='scp ${files} ${root}@${system}:${destination}'

    case $MANGLE_ENABLED in
	Yes|yes)
	    ;;
	No|no)
	    MANGLE_ENABLED=
	    ;;
	*)
	    if [ -n "$MANGLE_ENABLED" ]; then
		echo "   ERROR: Invalid MANGLE_ENABLED setting ($MANGLE_ENABLED)" >&2
		exit 2
	    fi
	    ;;
    esac
}

#
# Run the appropriate compiler
#
compiler() {
    local sc
    sc=${SHELLSHAREDIR}/compiler
    local pc
    pc=${PERLSHAREDIR}/compiler.pl

    startup_error() {
	echo "   ERROR: $@" >&2
	exit 1
    }
    
    local command
    command=$1

    shift
    
    if [ $(id -u) -ne 0 ]; then
	if [ -z "$SHOREWALL_DIR" -o "$SHOREWALL_DIR" = /etc/shorewall6 ]; then
	    startup_error "Ordinary users may not compile the /etc/shorewall6 configuration"
	fi
    fi
    #
    # We've now set SHOREWALL_DIR so recalculate CONFIG_PATH
    #
    ensure_config_path

    compiler=perl
    haveparams=

    case $COMMAND in
	*start|try|refresh)
	    ;;
	*)
	    STARTUP_LOG=
	    LOG_VERBOSITY=-1
	    ;;
    esac

    [ $command = exec ] || command=
    
    debugflags="-w"
    [ -n "$DEBUG" ]   && debugflags='-wd'
    [ -n "$PROFILE" ] && debugflags='-wd:DProf'

    # Perl compiler only takes the output file as a argument
	    
    [ "$1" = debug -o "$1" = trace ]  && shift;
    [ "$1" = nolock ] && shift;
    shift 

    options="--verbose=$VERBOSE"
    [ -n "$STARTUP_LOG" ] && options="$options --log=$STARTUP_LOG"
    [ -n "$LOG_VERBOSITY" ] && options="$options --log_verbosity=$LOG_VERBOSITY";
    [ -n "$EXPORT" ] && options="$options --export"
    [ -n "$SHOREWALL_DIR" ] && options="$options --directory=$SHOREWALL_DIR"
    [ -n "$TIMESTAMP" ] && options="$options --timestamp"
    [ -n "$TEST" ] && options="$options --test"
    [ "$debugging" = trace ] && options="$options --debug"
    [ -n "$REFRESHCHAINS" ] && options="$options --refresh=$REFRESHCHAINS"
    [ -x $pc ] || startup_error "Shorewall6 requires the shorewall-perl package which is not installed"
    #
    # Run the appropriate params file
    #
    if [ -z "$haveparams" ]; then
	set -a; 
	run_user_exit params
	set +a
    fi

    $command perl $debugflags $pc $options $@
}    

#
# Start Command Executor
#
start_command() {
    local finished
    finished=0

    do_it() {
	local rc
	rc=0

	progress_message3 "Compiling..."

	if compiler run $debugging $nolock compile ${VARDIR}/.start; then
	    [ -n "$nolock" ] || mutex_on
	    ${VARDIR}/.start $debugging start
	    rc=$?
	    [ -n "$nolock" ] || mutex_off
	else
	    rc=$?
	    logger -p kern.err "ERROR:Shorewall6 start failed"
	fi

	exit $rc
    }

    if shorewall6_is_started; then
	error_message "Shorewall6 is already running"
	exit 0
    fi

    [ -n "$STARTUP_ENABLED" ] || fatal_error "Startup is disabled"

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			d*)
			    DEBUG=Yes
			    option=${option#d}
			    ;;
			f*)
			    FAST=Yes
			    option=${option#f}
			    ;;
			p*)
			    [ -n "$(which conntrack)" ] || fatal_error "The '-p' option requires the conntrack utility which does not appear to be installed on this system"
			    PURGE=Yes
			    option=${option%p}
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	0)
	    ;;
	1)
	    [ -n "$SHOREWALL_DIR" -o -n "$FAST" ] && usage 2

	    if [ ! -d $1 ]; then
		if [ -e $1 ]; then
		    echo "$1 is not a directory" >&2 && exit 2
		else
		    echo "Directory $1 does not exist" >&2 && exit 2
		fi
	    fi

	    SHOREWALL_DIR=$(resolve_file $1)
	    export SHOREWALL_DIR
	    ;;
	*)
	    usage 1
	    ;;
    esac

    export NOROUTES
    export PURGE

    if [ -n "$FAST" ]; then
	if qt mywhich make; then
	    #
	    # RESTOREFILE is exported by get_config()
	    #
	    make -qf ${CONFDIR}/Makefile || FAST=
	fi

	if [ -n "$FAST" ]; then

	    RESTOREPATH=${VARDIR}/$RESTOREFILE

	    if [ -x $RESTOREPATH ]; then
		if [ -x ${RESTOREPATH}-ipsets ]; then
		    echo Restoring Ipsets...
		    #
		    # We must purge iptables to be sure that there are no
		    # references to ipsets
		    #
		    iptables -F
		    iptables -X
		    $SHOREWALL_SHELL ${RESTOREPATH}-ipsets
		fi

		echo Restoring Shorewall6...
		$SHOREWALL_SHELL $RESTOREPATH restore
		date > ${VARDIR}/restarted
		progress_message3 Shorewall6 restored from $RESTOREPATH
	    else
		do_it
	    fi
	else
	    do_it
	fi
    else
	do_it
    fi
}

#
# Compile Command Executor
#
compile_command() {
    local finished
    finished=0

    while [ $finished -eq 0 ]; do
	[ $# -eq 0 ] && usage 1
	option=$1
	case $option in
	    -*)
		shift
		option=${option#-}

		[ -z "$option" ] && usage 1

		while [ -n "$option" ]; do
		    case $option in
			e*)
			    EXPORT=Yes
			    option=${option#e}
			    ;;
			p*)
			    PROFILE=Yes
			    option=${option#p}
			    ;;
			t*)
			    TEST=Yes
			    option=${option#t}
			    ;;			    
			d*)
			    DEBUG=Yes;
			    option=${option#d}
			    ;;
			-)
			    finished=1
			    option=
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		;;
	    *)
		finished=1
		;;
	esac
    done

    file=

    case $# in
	1)
	    file=$1
	    [ -d $file ] && echo "   ERROR: $file is a directory" >&2 && exit 2;
	    ;;
	2)
	    [ -n "$SHOREWALL_DIR" ] && usage 2

	    if [ ! -d $1 ]; then
		if [ -e $1 ]; then
		    echo "$1 is not a directory" >&2 && exit 2
		else
		    echo "Directory $1 does not exist" >&2 && exit 2
		fi
	    fi

	    SHOREWALL_DIR=$(resolve_file $1)
	    export SHOREWALL_DIR
	    file=$2
	    ;;
	*)
	    usage 1
	    ;;
    esac

    export EXPORT

    progress_message3 "Compiling..."

    compiler exec $debugging compile $file
}

#
# Check Command Executor
#
check_command() {
    local finished
    finished=0

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			e*)
			    EXPORT=Yes
			    option=${option#e}
			    ;;
			p*)
			    PROFILE=Yes
			    option=${option#p}
			    ;;
			d*)
			    DEBUG=Yes;
			    option=${option#d}
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	0)
	    ;;
	1)
	    [ -n "$SHOREWALL_DIR" ] && usage 2

	    if [ ! -d $1 ]; then
		if [ -e $1 ]; then
		    echo "$1 is not a directory" >&2 && exit 2
		else
		    echo "Directory $1 does not exist" >&2 && exit 2
		fi
	    fi

	    SHOREWALL_DIR=$(resolve_file $1)
	    export SHOREWALL_DIR
	    ;;
	*)
	    usage 1
	    ;;
    esac

    export EXPORT

    progress_message3 "Checking..."

    compiler exec $debugging $nolock check
}

#
# Restart Command Executor
#
restart_command() {
    local finished
    finished=0
    local rc
    rc=0

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			d*)
			    DEBUG=Yes
			    option=${option#d}
			    ;;
			f*)
			    FAST=Yes
			    option=${option#f}
			    ;;
			n*)
			    NOROUTES=Yes
			    option=${option#n}
			    ;;
			p*)
			    [ -n "$(which conntrack)" ] || fatal_error "The '-p' option requires the conntrack utility which does not appear to be installed on this system"
			    PURGE=Yes
			    option=${option%p}
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	0)
	    ;;
	1)
	    [ -n "$SHOREWALL_DIR" ] && usage 2

	    if [ ! -d $1 ]; then
		if [ -e $1 ]; then
		    echo "$1 is not a directory" >&2 && exit 2
		else
		    echo "Directory $1 does not exist" >&2 && exit 2
		fi
	    fi

	    SHOREWALL_DIR=$(resolve_file $1)
	    [ -n "$FAST" ] && fatal_error "Directory may not be specified with the -f option"
	    export SHOREWALL_DIR
	    ;;
	*)
	    usage 1
	    ;;
    esac

    [ -n "$STARTUP_ENABLED" ] || fatal_error "Startup is disabled"

    export NOROUTES
    export PURGE

   if [ -z "$FAST" ]; then  
       progress_message3 "Compiling..."

       if compiler run $debugging $nolock compile ${VARDIR}/.restart; then
	   [ -n "$nolock" ] || mutex_on
	   $SHOREWALL_SHELL ${VARDIR}/.restart $debugging restart
	   rc=$?
	   [ -n "$nolock" ] || mutex_off
       else
	   rc=$?
	   logger -p kern.err "ERROR:Shorewall6 restart failed"
       fi
   else
       [ -x ${VARDIR}/.restore ] || fatal_error "No ${VARDIR}/.restore file found"
       [ -n "$nolock" ] || mutex_on
       $SHOREWALL_SHELL ${VARDIR}/.restore $debugging restart
       rc=$?
       [ -n "$nolock" ] || mutex_off
   fi

   return $rc
}

#
# Refresh Command Executor
#
refresh_command() {
    local finished
    finished=0

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    if [ $# -gt 0 ]; then
	REFRESHCHAINS=$1
	shift

	while [ $# -gt 0 ]; do
	    REFRESHCHAINS="$REFRESHCHAINS,$1"
	    shift
	done
    fi

    shorewall6_is_started || fatal_error "Shorewall6 is not running"

    [ -n "$STARTUP_ENABLED" ] || fatal_error "Startup is disabled"

    export NOROUTES

    progress_message3 "Compiling..."

    if compiler run $debugging $nolock compile ${VARDIR}/.refresh; then
	[ -n "$nolock" ] || mutex_on
	$SHOREWALL_SHELL ${VARDIR}/.refresh $debugging refresh
	rc=$?
	[ -n "$nolock" ] || mutex_off
    else
	rc=$?
    fi

    return $rc
}

#
# Safe-start/safe-restart Command Executor
#
safe_commands() {
    local finished
    finished=0

    # test is the shell supports timed read
    read -t 0 junk 2> /dev/null
    if [ $? -eq 2 -a ! -x /bin/bash ];then
	echo "Your shell does not support a feature required to execute this command".
	exit 2
    fi

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			n*)
			    NOROUTES=Yes
			    option=${option#n}
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	0)
	    ;;
	1)
	    [ -n "$SHOREWALL_DIR" ] && usage 2

	    if [ ! -d $1 ]; then
		if [ -e $1 ]; then
		    echo "$1 is not a directory" >&2 && exit 2
		else
		    echo "Directory $1 does not exist" >&2 && exit 2
		fi
	    fi

	    SHOREWALL_DIR=$(resolve_file $1)
	    export SHOREWALL_DIR
	    ;;
	*)
	    usage 1
	    ;;
    esac

    [ -n "$STARTUP_ENABLED" ] || fatal_error "Startup is disabled"

    if shorewall6_is_started; then
	running=Yes
    else
	running=
    fi

    if [ "$COMMAND" = "safe-start" -a  -n "$running" ]; then
	# the command is safe-start but the firewall is already running
	error_message "Shorewall6 is already started"
	exit 0
    fi

    if [ "$COMMAND" = "safe-start" -o -z "$running" ]; then
	# the command is safe-start or shorewall6 is not started yet
	command="start"
    else
	# the command is safe-restart and the firewall is already running
	command="restart"
    fi

    progress_message3 "Compiling..."

    if ! compiler run $debugging nolock compile ${VARDIR}/.$command; then
	status=$?
	exit $status
    fi

    case $command in
	start)
	    export RESTOREFILE=NONE
	    progress_message3 "Starting..."
	    ;;
	restart)
	    export RESTOREFILE=.safe
	    RESTOREPATH=${VARDIR}/.safe
	    save_config
	    progress_message3 "Restarting..."
	    ;;
    esac

    [ -n "$nolock" ] || mutex_on

    if ${VARDIR}/.$command $command; then

	echo -n "Do you want to accept the new firewall configuration? [y/n] "

	if read_yesno_with_timeout; then
	    echo "New configuration has been accepted"
	else
	    if [ "$command" = "restart" ]; then
		${VARDIR}/.safe restore
	    else
		${VARDIR}/.$command clear
	    fi
	    
	    [ -n "$nolock" ] || mutex_off

	    echo "New configuration has been rejected and the old one restored"
	    exit 2
	fi

    fi

    [ -n "$nolock" ] || mutex_off
}

#
# 'try' Command Executor
#
try_command() {
    local finished
    finished=0
    local timeout
    timeout=

    handle_directory() {
	[ -n "$SHOREWALL_DIR" ] && usage 2

	if [ ! -d $1 ]; then
	    if [ -e $1 ]; then
		echo "$1 is not a directory" >&2 && exit 2
	    else
		echo "Directory $1 does not exist" >&2 && exit 2
	    fi
	fi
	
	SHOREWALL_DIR=$(resolve_file $1)
	export SHOREWALL_DIR
    }

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			n*)
			    NOROUTES=Yes
			    option=${option#n}
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	0)
	    usage 1
	    ;;
	1)
	    handle_directory $1
	    ;;
	2)
	    handle_directory $1
	    timeout=$2
	    case $timeout in 
		*[!0-9]*)
                    echo "   ERROR: Invalid timeout ($timeout)" >&2;
		    exit 1
		    ;;
	    esac
	    ;;
	*)
	    usage 1
	    ;;
    esac

    [ -n "$STARTUP_ENABLED" ] || fatal_error "Startup is disabled"

    if shorewall6_is_started; then
	running=Yes
    else
	running=
    fi

    if [ -z "$running" ]; then
	# shorewall6 is not started yet
	command="start"
    else
	# the firewall is already running
	command="restart"
    fi

    progress_message3 "Compiling..."

    if ! compiler run $debugging $nolock compile ${VARDIR}/.$command; then
	status=$?
	exit $status
    fi

    case $command in
	start)
	    export RESTOREFILE=NONE
	    progress_message3 "Starting..."
	    ;;
	restart)
	    export RESTOREFILE=.try
	    RESTOREPATH=${VARDIR}/.try
	    save_config
	    progress_message3 "Restarting..."
	    ;;
    esac

    [ -n "$nolock" ] || mutex_on

    if ${VARDIR}/.$command $command && [ -n "$timeout" ]; then
	sleep $timeout
	    
	if [ "$command" = "restart" ]; then
	    ${VARDIR}/.try restore
	else
	    ${VARDIR}/.$command clear
	fi	    
    fi

    [ -n "$nolock" ] || mutex_off

    return 0
}

rsh_command() {
    command="$*"

    eval $RSH_COMMAND
}

rcp_command() {
    files="$1"
    destination=$2
    
    eval $RCP_COMMAND
}

#
# [Re]load command executor
#
reload_command() # $* = original arguments less the command.
{
    local verbose
    verbose=$(make_verbose)
    local file
    file=
    local capabilities
    capabilities=
    local finished
    finished=0
    local saveit
    saveit=
    local result
    local directory
    local system
    local getcaps
    getcaps=
    local root
    root=root
    local compiler
    compiler=

    LITEDIR=/var/lib/shorewall6-lite

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			s*)
			    saveit=Yes
			    option=${option#s}
			    ;;
			c*)
			    getcaps=Yes
			    option=${option#c}
			    ;;
			r)
			    [ $# -gt 1 ] || fatal_error "Missing Root User name"
			    root=$2
			    option=
			    shift
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	1)
	    directory="."
	    system=$1
	    ;;
	2)
	    directory=$1
	    system=$2
	    ;;
	*)
	    usage 1
	    ;;
    esac

    litedir=$(rsh_command /sbin/shorewall6-lite show config 2> /dev/null | grep ^LITEDIR | sed 's/LITEDIR is //')

    [ -n "$litedir" ] && LITEDIR=$litedir

    if [ -z "$getcaps" ]; then
	SHOREWALL_DIR=$(resolve_file $directory)
	ensure_config_path
	capabilities=$(find_file capabilities)
	[ -f $capabilities ] || getcaps=Yes
    fi

    if [ -n "$getcaps" ]; then
	if [ -f $directory/shorewall6.conf ]; then
	    . $directory/shorewall6.conf
	    ensure_config_path
	fi

	progress_message "Getting Capabilities on system $system..."
	if ! rsh_command "MODULESDIR=$MODULESDIR MODULE_SUFFIX=\"$MODULE_SUFFIX\" IPTABLES=$IPTABLES /usr/share/shorewall6-lite/shorecap" > $directory/capabilities; then
	    fatal_error "ERROR: Capturing capabilities on system $system failed"
	fi
    fi

    file=$(resolve_file $directory/firewall)

    [ -n "$TIMESTAMP" ] && timestamp='-t' || timestamp=

    if shorewall6 $debugging $verbose $timestamp compile -e $compiler $directory $directory/firewall && \
	progress_message3 "Copying $file and ${file}.conf to ${system}:${LITEDIR}..." && \
	rcp_command "$directory/firewall $directory/firewall.conf" ${LITEDIR}
    then
	save=$(find_file save);

	[ -f $save ] && progress_message3 "Copying $save to ${system}:/etc/shorewall6-lite/" && rcp_command $save /etc/shorewall6-lite/

	progress_message3 "Copy complete"
	if [ $COMMAND = reload ]; then
	    rsh_command "/sbin/shorewall6-lite $debugging $verbose $timestamp restart" && \
	    progress_message3 "System $system reloaded" || saveit=
	else
	    rsh_command "/sbin/shorewall6-lite $debugging $verbose $timestamp start" && \
	    progress_message3 "System $system loaded" || saveit=
	fi

	if [ -n "$saveit" ]; then
	    rsh_command "/sbin/shorewall6-lite $debugging $verbose $timestamp save" && \
	    progress_message3 "Configuration on system $system saved"
	fi
    fi
}

#
# Export command executor
#
export_command() # $* = original arguments less the command.
{
    local verbose
    verbose=$(make_verbose) 
    local file
    file= 
    local finished
    finished=0 
    local directory 
    local target
    local compiler
    compiler=

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			*)
			    fatal_error "Unrecognized option \"$option\""
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    case $# in
	1)
	    directory="."
	    target=$1
	    ;;
	2)
	    directory=$1
	    target=$2
	    ;;
	*)
	    fatal_error "ERROR: Invalid command syntax (\"man shorewall6\" for help)"
	    ;;
    esac

    case $target in
	*:*)
	    ;;
	*)
	    target=$target:
	    ;;
    esac

    file=$(resolve_file $directory/firewall)

    if shorewall6 $debugging $verbose compile -e $compiler $directory $directory/firewall && \
	echo "Copying $file and ${file}.conf to ${target#*@}..." && \
	scp $directory/firewall $directory/firewall.conf $target
    then
	save=$(find_file save);

	[ -f $save ] && progress_message3 "Copying $save to ${target#*}..." && rcp_command $save $target

	progress_message3 "Copy complete"
    fi
}

#
# Give Usage Information
#
usage() # $1 = exit status
{
    echo "Usage: $(basename $0) [debug|trace] [nolock] [ -q ] [ -v[-1|{0-2}] ] [ -t ] <command>"
    echo "where <command> is one of:"
    echo "   add <interface>[:<host-list>] ... <zone>"
    echo "   allow <address> ..."
    echo "   check [ -e ] [ <directory> ]"
    echo "   clear [ -f ]"
    echo "   compile [ -e ] [ <directory name> ] <path name>"
    echo "   delete <interface>[:<host-list>] ... <zone>"
    echo "   drop <address> ..."
    echo "   dump [ -x ]"
    echo "   export [ <directory1> ] [<user>@]<system>[:<directory2>]"
    echo "   forget [ <file name> ]"
    echo "   help"
    echo "   load [ -s ] [ -c ] [ -r <root user> ] [ -C {shell|perl} ] [ <directory> ] <system>"
    echo "   logdrop <address> ..."
    echo "   logreject <address> ..."
    echo "   logwatch [<refresh interval>]"
    echo "   refresh [ <chain>... ]"
    echo "   reject <address> ..."
    echo "   reload [ -s ] [ -c ] [ -r <root user> ] [ <directory> ] <system>"
    echo "   reset [ <chain> ... ]"
    echo "   restart [ -n ] [ -p ] [ -f ] [ <directory> ]"
    echo "   restore [ -n ] [ <file name> ]"
    echo "   save [ <file name> ]"
    echo "   show [ -x ] [ -m ] [-f] [ -t {filter|mangle} ] [ {chain [<chain> [ <chain> ... ]|actions|capabilities|classifiers|config|connections|filters|ip|log|macros|mangle|nat|routing|tc|vardir|zones} ]"
    echo "   start [ -f ] [ -n ] [ -p ] [ <directory> ]"
    echo "   stop [ -f ]"
    echo "   status"
    echo "   try [ -C {shell|perl} ] <directory> [ <timeout> ]"
    echo "   version [ -a ]"
    echo "   safe-start [ -C {shell|perl} ] [ <directory> ]"
    echo "   safe-restart [ -C {shell|perl} ] [ <directory> ]"
    echo
    exit $1
}

#
# Execution begins here
#
debugging=

if [ $# -gt 0 ] && [ "x$1" = "xdebug" -o "x$1" = "xtrace" ]; then
    debugging=$1
    shift
fi

nolock=

if [ $# -gt 0 ] && [ "$1" = "nolock" ]; then
    nolock=nolock
    shift
fi

SHOREWALL_DIR=
IPT_OPTIONS="-nv"
FAST=
VERBOSE_OFFSET=0
USE_VERBOSITY=
NOROUTES=
PURGE=
EXPORT=
export TIMESTAMP=
noroutes=

finished=0

while [ $finished -eq 0 ]; do
    [ $# -eq 0 ] && usage 1
    option=$1
    case $option in
	-)
	    finished=1
	    ;;
	-*)
	    option=${option#-}

	    while [ -n "$option" ]; do
		case $option in
		    c)
			[ $# -eq 1 ] && usage 1

			if [ ! -d $2 ]; then
			    if [ -e $2 ]; then
				echo "$2 is not a directory" >&2 && exit 2
			    else
				echo "Directory $2 does not exist" >&2 && exit 2
			    fi
			fi

			SHOREWALL_DIR=$(resolve_file $2)
			option=
			shift
			;;
		    e*)
			EXPORT=Yes
			option=${option#e}
			;;
		    x*)
			IPT_OPTIONS="-xnv"
			option=${option#x}
			;;
		    q*)
			VERBOSE_OFFSET=$(($VERBOSE_OFFSET - 1 ))
			option=${option#q}
			;;
		    f*)
			FAST=Yes
			option=${option#f}
			;;
		    v*)
			option=${option#v}
			case $option in 
			    -1*)
				USE_VERBOSITY=-1
				option=${option#-1}
				;;
			    0*)
				USE_VERBOSITY=0
				option=${option#0}
				;;
			    1*)
				USE_VERBOSITY=1
				option=${option#1}
				;;
			    2*)
				USE_VERBOSITY=2
				option=${option#2}
				;;
			    *)
				VERBOSE_OFFSET=$(($VERBOSE_OFFSET + 1 ))
				USE_VERBOSITY=
				;;
			esac
			;;
		    n*)
			NOROUTES=Yes
			option=${option#n}
			;;
		    t*)
			TIMESTAMP=Yes
			option=${option#t}
			;;
		    -)
			finished=1
			option=
			;;
		    *)
			usage 1
			;;
		esac
	    done
	    shift
	    ;;
	*)
	    finished=1
            ;;
    esac
done

version_command() {
    local finished
    finished=0
    local all
    all=

    while [ $finished -eq 0 -a $# -gt 0 ]; do
	option=$1
	case $option in
	    -*)
		option=${option#-}

		while [ -n "$option" ]; do
		    case $option in
			-)
			    finished=1
			    option=
			    ;;
			a*)
			    all=Yes
			    option=${option#a}
			    ;;
			*)
			    usage 1
			    ;;
		    esac
		done
		shift
		;;
	    *)
		finished=1
		;;
	esac
    done

    [ $# -gt 0 ] && usage 1

    echo $version

    if [ -n "$all" ]; then
	if [ -f /usr/share/shorewall-perl/version ]; then
	    echo "Shorewall-perl $(cat /usr/share/shorewall-perl/version)"
	fi
    fi
}

if [ $# -eq 0 ]; then
    usage 1
fi

[ -n "$SHOREWALL_DIR" ] && export SHOREWALL_DIR
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin
MUTEX_TIMEOUT=

SHAREDIR=/usr/share/shorewall6
CONFDIR=/etc/shorewall6
export PRODUCT="Shorewall6"

[ -f ${CONFDIR}/vardir ] && . ${CONFDIR}/vardir

[ -n "${VARDIR:=/var/lib/shorewall6}" ]

FIREWALL=$SHAREDIR/firewall
LIBRARIES="$SHAREDIR/lib.base $SHAREDIR/lib.cli"
VERSION_FILE=$SHAREDIR/version
REFRESHCHAINS=

for library in $LIBRARIES; do
    if [ -f $library ]; then
	. $library
    else
	echo "$library does not exist!" >&2
	exit 2
    fi
done

if [ ! -f $FIREWALL ]; then
    echo "   ERROR: Shorewall6 is not properly installed" >&2
    if [ -L $FIREWALL ]; then
	echo "	     $FIREWALL is a symbolic link to a" >&2
	echo "	     non-existant file" >&2
    else
	echo "	     The file $FIREWALL does not exist" >&2
    fi

    exit 2
fi

if [ -f $VERSION_FILE ]; then
    version=$(cat $VERSION_FILE)
else
    echo "   ERROR: Shorewall6 is not properly installed" >&2
    echo "	 The file $VERSION_FILE does not exist"  >&2
    exit 1
fi

banner="Shorewall6-$version Status at $HOSTNAME -"

case $(echo -e) in
    -e*)
	RING_BELL="echo \a"
	ECHO_E="echo"
	;;
    *)
	RING_BELL="echo -e \a"
	ECHO_E="echo -e"
	;;
esac

case $(echo -n "Testing") in
    -n*)
	ECHO_N=
	;;
    *)
	ECHO_N=-n
	;;
esac

COMMAND=$1

case "$COMMAND" in
    start)
	get_config Yes Yes
	shift
	start_command $@
	;;
    stop|clear)
	if [ "x$2" = x-f ]; then
	    [ -x ${VARDIR}/.restore ] && FIREWALL=${VARDIR}/.restore
	    shift;
	fi

	[ $# -ne 1 ] && usage 1
	get_config
	export NOROUTES
	mutex_on
	$SHOREWALL_SHELL $FIREWALL $debugging $nolock $COMMAND
	mutex_off
	;;
    reset)
	get_config
	export NOROUTE
	shift
	mutex_on
	$SHOREWALL_SHELL $FIREWALL $debugging $nolock reset $@
	mutex_off
	;;
    compile)
	get_config Yes
	shift
	compile_command $@
	;;
    restart)
	get_config Yes Yes
	shift
	restart_command $@
	;;
    refresh)
	get_config Yes Yes
	shift
	refresh_command $@
	;;
    check)
	get_config Yes
	shift
	check_command $@
	;;
    add|delete)
	[ $# -lt 3 ] && usage 1
	get_config
	mutex_on
	$SHOREWALL_SHELL $FIREWALL $debugging $nolock $@
	mutex_off
	;;
    show|list)
	get_config Yes No Yes
    	shift
    	show_command $@
	;;
    load|reload)
	get_config Yes
	shift
	reload_command $@
	;;
    export)
	get_config Yes
	shift
	export_command $@
	;;
    status)
	[ $# -eq 1 ] || usage 1
	get_config
	echo "Shorewall6-$version Status at $HOSTNAME - $(date)"
	echo
	if shorewall6_is_started ; then
	    echo "Shorewall6 is running"
	    status=0
	else
	    echo "Shorewall6 is stopped"
	    status=4
	fi

	if [ -f ${VARDIR}/state ]; then
	    state="$(cat ${VARDIR}/state)"
	    case $state in
		Stopped*|Clear*)
		    status=3
		    ;;
	    esac
	else
	    state=Unknown
	fi
	echo "State:$state"
	echo
	exit $status
	;;
    dump)
	get_config Yes No Yes
    	shift
    	dump_command $@
	;;
    version)
	shift
	version_command $@
	;;
    try)
	get_config Yes
	shift
	try_command $@
	;;
    logwatch)
	get_config Yes Yes Yes
	banner="Shorewall6-$version Logwatch at $HOSTNAME -"
	logwatch_command $@
	;;
    drop)
	get_config
	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] && usage 1
	if shorewall6_is_started ; then
	    [ -n "$nolock" ] || mutex_on
	    block DROP Dropped $*
	    [ -n "$nolock" ] || mutex_off
	else
	    fatal_error "Shorewall6 is not started"
	fi
	;;
    logdrop)
	get_config
	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] && usage 1
	if shorewall6_is_started ; then
	    [ -n "$nolock" ] || mutex_on
	    block logdrop Dropped $*
	    [ -n "$nolock" ] || mutex_off
	else
	    fatal_error "Shorewall6 is not started"
	fi
	;;
    reject|logreject)
	get_config
	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] && usage 1
	if shorewall6_is_started ; then
	    [ -n "$nolock" ] || mutex_on
	    block $COMMAND Rejected $*
	    [ -n "$nolock" ] || mutex_off
	else
	    fatal_error "Shorewall6 is not started"
	fi
	;;
    allow)
	get_config
	allow_command $@
	;;
    save)
	get_config
	[ -n "$debugging" ] && set -x

	case $# in
	1)
	    ;;
	2)
	    RESTOREFILE="$2"
	    validate_restorefile '<restore file>'
	    ;;
	*)
	    usage 1
	    ;;
	esac

	RESTOREPATH=${VARDIR}/$RESTOREFILE

	[ -n "$nolock" ] || mutex_on

	save_config

	result=$?

	[ -n "$nolock" ] || mutex_off

	exit $result
	;;
    forget)
	get_config
	case $# in
	1)
	    ;;
	2)
	    RESTOREFILE="$2"
	    validate_restorefile '<restore file>'
	    ;;
	*)
	    usage 1
	    ;;
	esac


	RESTOREPATH=${VARDIR}/$RESTOREFILE

	if [ -x $RESTOREPATH ]; then

	    if [ -x ${RESTOREPATH}-ipsets ]; then
		rm -f ${RESTOREPATH}-ipsets
		echo "    ${RESTOREPATH}-ipsets removed"
	    fi

	    rm -f $RESTOREPATH
	    rm -f ${RESTOREPATH}-iptables
	    echo "    $RESTOREPATH removed"
	elif [ -f $RESTOREPATH ]; then
	    echo "   $RESTOREPATH exists and is not a saved Shorewall6 configuration"
	fi
	rm -f ${VARDIR}/save
	;;
    restore)
	get_config
	shift
	restore_command $@
        ;;
    call)
	get_config
	[ -n "$debugging" ] && set -x
	#
	# Undocumented way to call functions in ${SHAREDIR}/functions directly
	#
	shift
	$@
	;;
    help)
	shift
	usage
	;;
    safe-restart|safe-start)
	get_config Yes
	shift
	safe_commands $@
	;;
    *)
	usage 1
	;;

esac