#!/bin/sh
#
# Shorewall 4.4 -- /usr/share/shorewall/lib.base
#
#     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 - Tom Eastep (teastep@shorewall.net)
#
#	Complete documentation is available at http://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.
#
# This library contains the code common to all Shorewall components.
#
# - It is loaded by /sbin/shorewall.
# - It is released as part of Shorewall Lite where it is used by /sbin/shorewall-lite
#   and /usr/share/shorewall-lite/shorecap.
#

SHOREWALL_LIBVERSION=40407
SHOREWALL_CAPVERSION=40417

[ -n "${VARDIR:=/var/lib/shorewall}" ]
[ -n "${SHAREDIR:=/usr/share/shorewall}" ]
[ -n "${CONFDIR:=/etc/shorewall}" ]

#
# Conditionally produce message
#
progress_message() # $* = Message
{
    local timestamp
    timestamp=

    if [ $VERBOSITY -gt 1 ]; then
	[ -n "$g_timestamp" ] && timestamp="$(date +%H:%M:%S) "
	echo "${timestamp}$@"
    fi
}

progress_message2() # $* = Message
{
    local timestamp
    timestamp=

    if [ $VERBOSITY -gt 0 ]; then
	[ -n "$g_timestamp" ] && timestamp="$(date +%H:%M:%S) "
	echo "${timestamp}$@"
    fi
}

progress_message3() # $* = Message
{
    local timestamp
    timestamp=

    if [ $VERBOSITY -ge 0 ]; then
	[ -n "$g_timestamp" ] && timestamp="$(date +%H:%M:%S) "
	echo "${timestamp}$@"
    fi
}

#
# Undo the effect of 'separate_list()'
#
combine_list()
{
    local f
    local o
    o=

    for f in $* ; do
        o="${o:+$o,}$f"
    done

    echo $o
}

#
# Call this function to assert mutual exclusion with Shorewall. If you invoke the
# /sbin/shorewall program while holding mutual exclusion, you should pass "nolock" as
# the first argument. Example "shorewall nolock refresh"
#
# This function uses the lockfile utility from procmail if it exists.
# Otherwise, it uses a somewhat race-prone algorithm to attempt to simulate the
# behavior of lockfile.
#
mutex_on()
{
    local try
    try=0
    local lockf
    lockf=${LOCKFILE:=${VARDIR}/lock}

    MUTEX_TIMEOUT=${MUTEX_TIMEOUT:-60}

    if [ $MUTEX_TIMEOUT -gt 0 ]; then

	[ -d ${VARDIR} ] || mkdir -p ${VARDIR}

	if qt mywhich lockfile; then
	    lockfile -${MUTEX_TIMEOUT} -r1 ${lockf}
	else
	    while [ -f ${lockf} -a ${try} -lt ${MUTEX_TIMEOUT} ] ; do
		sleep 1
		try=$((${try} + 1))
	    done

	    if  [ ${try} -lt ${MUTEX_TIMEOUT} ] ; then
	        # Create the lockfile
		echo $$ > ${lockf}
	    else
		echo "Giving up on lock file ${lockf}" >&2
	    fi
	fi
    fi
}

#
# Call this function to release mutual exclusion
#
mutex_off()
{
    rm -f ${LOCKFILE:=${VARDIR}/lock}
}

#
# Find the interface with the passed MAC address
#

find_interface_by_mac() {
    local mac
    mac=$1
    local first
    local second
    local rest
    local dev

    $IP link list | while read first second rest; do
	case $first in
	    *:)
                dev=$second
		;;
	    *)
	        if [ "$second" = $mac ]; then
		    echo ${dev%:}
		    return
		fi
	esac
    done
}

[ -z "$LEFTSHIFT" ] && . ${SHAREDIR}/lib.common

#
# Validate an IP address
#
valid_address() {
    local x
    local y
    local ifs
    ifs=$IFS

    IFS=.

    for x in $1; do
	case $x in
	    [0-9]|[0-9][0-9]|[1-2][0-9][0-9])
		[ $x -lt 256 ] || { IFS=$ifs; return 2; }
                ;;
	    *)
	        IFS=$ifs
		return 2
		;;
	esac
    done

    IFS=$ifs

    return 0
}

#
# Miserable Hack to work around broken BusyBox ash in OpenWRT
#
addr_comp() {
    test $(bc <<EOF
$1 > $2
EOF
) -eq 1

}

#
# Enumerate the members of an IP range -- When using a shell supporting only
# 32-bit signed arithmetic, the range cannot span 128.0.0.0.
#
# Comes in two flavors:
#
# ip_range() - produces a mimimal list of network/host addresses that spans
#              the range.
#
# ip_range_explicit() - explicitly enumerates the range.
#
ip_range() {
    local first
    local last
    local l
    local x
    local y
    local z
    local vlsm

    case $1 in
	!*)
	    #
	    # Let iptables complain if it's a range
	    #
	    echo $1
	    return
	    ;;
	[0-9]*.*.*.*-*.*.*.*)
            ;;
	*)
	    echo $1
	    return
	    ;;
    esac

    first=$(decodeaddr ${1%-*})
    last=$(decodeaddr ${1#*-})

    if addr_comp $first $last; then
	fatal_error "Invalid IP address range: $1"
    fi

    l=$(( $last + 1 ))

    while addr_comp $l $first; do
	vlsm=
	x=31
	y=2
	z=1

	while [ $(( $first % $y )) -eq 0 ] && ! addr_comp $(( $first + $y )) $l; do
	    vlsm=/$x
	    x=$(( $x - 1 ))
	    z=$y
	    y=$(( $y * 2 ))
	done

	echo $(encodeaddr $first)$vlsm
	first=$(($first + $z))
    done
}

ip_range_explicit() {
    local first
    local last

    case $1 in
    [0-9]*.*.*.*-*.*.*.*)
	;;
    *)
	echo $1
	return
	;;
    esac

    first=$(decodeaddr ${1%-*})
    last=$(decodeaddr ${1#*-})

    if addr_comp $first $last; then
	fatal_error "Invalid IP address range: $1"
    fi

    while ! addr_comp $first $last; do
	echo $(encodeaddr $first)
	first=$(($first + 1))
    done
}

#
# Netmask to VLSM
#
ip_vlsm() {
    local mask
    mask=$(decodeaddr $1)
    local vlsm
    vlsm=0
    local x
    x=$(( 128 << 24 )) # 0x80000000

    while [ $(( $x & $mask )) -ne 0 ]; do
	[ $mask -eq $x ] && mask=0 || mask=$(( $mask $LEFTSHIFT 1 )) # Not all shells shift 0x80000000 left properly.
	vlsm=$(($vlsm + 1))
    done

    if [ $(( $mask & 2147483647 )) -ne 0 ]; then # 2147483647 = 0x7fffffff
	echo "Invalid net mask: $1" >&2
    else
	echo $vlsm
    fi
}

#
# Set default config path
#
ensure_config_path() {
    local F
    F=${SHAREDIR}/configpath
    if [ -z "$CONFIG_PATH" ]; then
	[ -f $F ] || { echo "   ERROR: $F does not exist"; exit 2; }
	. $F
    fi

    if [ -n "$SHOREWALL_DIR" ]; then
	[ "${CONFIG_PATH%%:*}" = "$SHOREWALL_DIR" ] || CONFIG_PATH=$SHOREWALL_DIR:$CONFIG_PATH
    fi
}

#
# Get fully-qualified name of file
#
resolve_file() # $1 = file name
{
    local pwd
    pwd=$PWD

    case $1 in
	/*)
	    echo $1
	    ;;
	.)
	    echo $pwd
	    ;;
	./*)
	    echo ${pwd}${1#.}
	    ;;
	..)
	    cd ..
	    echo $PWD
	    cd $pwd
	    ;;
	../*)
	    cd ..
	    resolve_file ${1#../}
	    cd $pwd
	    ;;
	*)
	    echo $pwd/$1
	    ;;
    esac
}

# Function to truncate a string -- It uses 'cut -b -<n>'
# rather than ${v:first:last} because light-weight shells like ash and
# dash do not support that form of expansion.
#

find_echo() {
    local result

    result=$(echo "a\tb")
    [ ${#result} -eq 3 ] && { echo echo; return; }

    result=$(echo -e "a\tb")
    [ ${#result} -eq 3 ] && { echo "echo -e"; return; }

    result=$(which echo)
    [ -n "$result" ] && { echo "$result -e"; return; }

    echo echo
}

# Determine which version of mktemp is present (if any) and set MKTEMP accortingly:
#
#     None - No mktemp
#     BSD  - BSD mktemp (Mandrake)
#     STD  - mktemp.org mktemp
#
find_mktemp() {
    local mktemp
    mktemp=`mywhich mktemp 2> /dev/null`

    if [ -n "$mktemp" ]; then
	if qt mktemp -V ; then
	    MKTEMP=STD
	else
	    MKTEMP=BSD
	fi
    else
	MKTEMP=None
    fi
}

#
# create a temporary file. If a directory name is passed, the file will be created in
# that directory. Otherwise, it will be created in a temporary directory.
#
mktempfile() {

    [ -z "$MKTEMP" ] && find_mktemp

    if [ $# -gt 0 ]; then
	case "$MKTEMP" in
	    BSD)
		mktemp $1/shorewall.XXXXXX
		;;
	    STD)
		mktemp -p $1 shorewall.XXXXXX
		;;
	    None)
		> $1/shorewall-$$ && echo $1/shorewall-$$
		;;
	    *)
		error_message "ERROR:Internal error in mktempfile"
		;;
	esac
    else
	case "$MKTEMP" in
	    BSD)
		mktemp /tmp/shorewall.XXXXXX
		;;
	    STD)
		mktemp -t shorewall.XXXXXX
		;;
	    None)
		rm -f /tmp/shorewall-$$
		> /tmp/shorewall-$$ && echo /tmp/shorewall-$$
		;;
	    *)
		error_message "ERROR:Internal error in mktempfile"
		;;
	esac
    fi
}