#! /bin/bash
if [ -t 1 ]; then
    reset
fi

BCHANNEL_LOG_DIR="/data/bchannellog"

SCRIPT_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
if [ -z "$toolsPath" ]; then
    toolsPath=${SCRIPT_DIR}
fi

doSudo=""
if [ $(id -un) != "root" ]; then
    doSudo="/usr/bin/sudo"
fi

export PATH=$PATH:${toolsPath}

function startDnsmasq() {
    workDir=$1

    if [ -d "${workDir}/dnsmasq" ]; then
        ${SCRIPT_DIR}/dnsmasq/enable "${workDir}/dnsmasq/*"
    fi
}

function stopDnsmasq() {
    if [ -d "${workDir}/dnsmasq" ]; then
        ${SCRIPT_DIR}/dnsmasq/disable
    fi
}

function restartSyslog() {
    sudo service rsyslog restart >/dev/null 2>/dev/null
}

function writeSeLinuxConfig() {
    cat >${SCRIPT_DIR}/testsuite.te <<EOF
module testsuite 1.5;

require {
	type syslogd_t;
	type usr_t;
        class dir write;
	class dir add_name;
	class file create;
        class file write;
        class file append;
}

#============= syslogd_t ==============

allow syslogd_t usr_t:dir add_name;
allow syslogd_t usr_t:dir write;
allow syslogd_t usr_t:file create;
allow syslogd_t usr_t:file write;
allow syslogd_t usr_t:file append;    
EOF
}

function checkSeLinux() {
    if selinuxenabled; then
        if ! semodule -l | grep -q testsuite; then
            writeSeLinuxConfig
            sudo checkmodule -M -m -o ${SCRIPT_DIR}/testsuite.mod ${SCRIPT_DIR}/testsuite.te
            sudo semodule_package -o ${SCRIPT_DIR}/testsuite.pp -m ${SCRIPT_DIR}/testsuite.mod
            sudo semodule -i ${SCRIPT_DIR}/testsuite.pp
        fi
    fi
}

function writeSyslogConfig() {
    checkSeLinux

    cat >51-test.conf <<EOF
\$template ferrariFormatKernel,"<%PRI%>%timereported:1:10:date-rfc3339% %timereported:12:22:date-rfc3339% %HOSTNAME% %msg%\n"
\$template ferrariFormat,"<%PRI%>%timereported:1:10:date-rfc3339% %timereported:12:22:date-rfc3339% %HOSTNAME% %msg%\n"
kern.*   ${logdir}/${base}/syslog.olg;ferrariFormatKernel
local5.*;auth.*;authpriv.*;cron.*;daemon.*;lpr.*;mail.*;mark.*;news.*;security.*;syslog.*;user.*;uucp.*;local0.*;local1.*;local2.*;local3.*;local4.*;local6.*;local7.*  ${logdir}/${base}/syslog.olg;ferrariFormat
EOF

    sudo mv 51-test.conf /etc/rsyslog.d/51-test.conf
    restartSyslog
}

function deleteSyslogConfig() {
    sudo rm -f /etc/rsyslog.d/51-test.conf
    restartSyslog
}

function overwrite() {
    if [ -t 1 ]; then
        echo -e "\r\033[1A\033[0K$@"
    fi
}

function setScenarioName() {
    scenario=$(grep "scenario name=" "${logdir}/${base}/sender1.xml" | sed -e "s/.*scenario name=\"\(.*\)\".*/\1/")
}

function echoStatus() {
    printf '%s "%s" %s\n' "$1" "${scenario}" "$2"
}

function echoJsonStatus() {
    echo -n -e "${jsonComma}{\"statusCode\": ${1}, \"statusText\": \"${2}\", \"scenario\": \"${scenario}\""}
    jsonComma=","
}

function showStatus() {
    if [ -t 1 -a -z "$json" ]; then
        output=$(printf '%s "%s" %s' "$1" "${scenario}" "$2")
        overwrite "${output}"
    fi
}

function echoJsonHeader() {
    if [ ! -z "$json" ]; then
        printf '{"tests":['
    fi
}

function echoJsonFooter() {
    if [ ! -z "$json" ]; then
        printf '], "status":%s}\n' "$overallTestState"
    fi
}

function writeXunitHeader() {
    echo '<?xml version="1.0" encoding="UTF-8"?>' >${xunitxml}
    echo '<testsuites time="@@TIME@@" tests="@@TESTS@@" failures="@@FAILURES@@">' >>${xunitxml}
    echo "    <testsuite failures=\"@@FAILURES@@\" tests=\"@@TESTS@@\" errors=\"@@FAILURES@@\" name=\"sipp\" time=\"@@TIME@@\">" >>${xunitxml}
}

function writeXunitFooter() {
    local duration=$1
    local numTests=$2
    local numFailures=$3
    echo '    </testsuite>' >>${xunitxml}
    echo '</testsuites>' >>${xunitxml}
    sed \
        -i \
        -e "s/@@TIME@@/${duration}/" \
        -e "s/@@TESTS@@/${numTests}/" \
        -e "s/@@FAILURES@@/${numFailures}/" \
        ${xunitxml}
}

function showPassedStatus() {
    local code=$1
    local message=$2

    rm ${logdir}/${base}/fail 2>/dev/null
    touch ${logdir}/${base}/ok

    if [ ! -z "$json" ]; then
        echoJsonStatus "${code}" "${message}"
    elif [ -t 1 ]; then
        output=$(printf '%s%-5s %-20s%s' "$(tput setaf 2)" "${code}" "${message}" "$(tput sgr0)")
        showStatus "$output"
        echo
    else
        echoStatus "${code}" "${message}"
    fi

    echo "       <testcase classname=\"sipp.sipp\" name=\"${scenario}\" time=\"${testTimeDelta}\"/>" >>${xunitxml}
}

function showFailedStatus() {
    local code=$1
    local message=$2

    rm ${logdir}/${base}/ok 2>/dev/null
    touch ${logdir}/${base}/fail
    if [ "$noTestStatus" != "true" ]; then
        overallTestState=3
    fi

    if [ ! -z "$json" ]; then
        echoJsonStatus "${code}" "${message}"
    elif [ -t 1 ]; then
        output=$(printf '%s%-5s %-20s%s' "$(tput setaf 1)" "${code}" "${message}" "$(tput sgr0)")
        showStatus "$output"
        echo
    else
        echoStatus "${code}" "${message}"
    fi

    echo "       <testcase classname=\"sipp.sipp\" name=\"${scenario}\" time=\"${testTimeDelta}\">" >>${xunitxml}
    echo "           <failure message=\"${code} ${message}\" type=\"AssertionError\"/>" >>${xunitxml}
    echo "       </testcase>" >>${xunitxml}
}

function moveLogs() {
    myid=$(id -u)
    mygroup=$(id -g)
    ${doSudo} chown -R $myid:$mygroup "${logdir}/${base}/"
    find "${logdir}/${base}/" -regex ".*_[0-9]*_.*" -exec sh -c 'src="{}"; dst=$(echo $src | sed -e "s/\(.*\)_[0-9]*_\(.*\)/\\1.\\2/"); mv "$src" "$dst"' \;
}

function clearBchannelLogs() {
    if [ -e ${BCHANNEL_LOG_DIR} ]; then
        fileCount=$(ls ${BCHANNEL_LOG_DIR} | wc -l)
        if [ "${fileCount}" != "0" ]; then
            ${doSudo} rm -rf ${BCHANNEL_LOG_DIR}/*
        fi
    fi
}

function moveBchannelLogs() {
    if [ -e ${BCHANNEL_LOG_DIR} ]; then
        fileCount=$(ls ${BCHANNEL_LOG_DIR} | wc -l)
        if [ "${fileCount}" != "0" ]; then
            mkdir -p "${logdir}/${base}/bchannellog/"
            ${doSudo} mv ${BCHANNEL_LOG_DIR}/* "${logdir}/${base}/bchannellog/" 2>/dev/null
            myid=$(id -u)
            mygroup=$(id -g)
            ${doSudo} chown -R $myid:$mygroup "${logdir}/${base}/bchannellog"
        fi
    fi
}

function startPre() {
    showStatus "prepare" "..."
    preRetval=501
    if test -e "${logdir}/${base}/pre"; then
        "${logdir}/${base}/pre" "${logdir}/${base}" >"${logdir}/${base}/pre.log" 2>&1
        preRetval=$?
    fi
}

function startPost() {
    showStatus "post process" "..."
    postRetval=502
    if test -e "${logdir}/${base}/post"; then
        "${logdir}/${base}/post" "${logdir}/${base}" $@ >"${logdir}/${base}/post.log" 2>&1
        postRetval=$?
    else
        postRetval=0
    fi
}

function killProcess() {
    exec 3>&2        # 3 is now a copy of 2
    exec 2>/dev/null # 2 now points to /dev/null

    # run script with redirected stderr
    bash -c "kill $1"
    sleep 1
    bash -c "kill -9 $1"

    exec 2>&3 # restore stderr to saved
    exec 3>&- # close saved version
}

function waitForProcessesWithTimeout() {
    local subTest=${1}
    local limit=${2}
    local pids=${@:3}
    local count=0
    while true; do
        local have_to_wait=false
        for pid in ${pids}; do
            if kill -0 ${pid} &>/dev/null; then
                have_to_wait=true
            else
                pids=$(echo ${pids} | sed -e "s/${pid}//g")
            fi
        done
        if ${have_to_wait} && (($count < $limit)); then
            count=$((count + 1))
            showStatus "testing step:${subTest} of" "... ${count}s of ${limit}s"
            sleep 1
        else
            if (($count >= $limit)); then
                showStatus "error step:${subTest} of" "timeout ${limit} reached"
            fi
            processList=${pids}
            return 1
        fi
    done
    return 0
}

function waitForTimeout() {
    local limit=${1}
    local count=0
    while (($count < $limit)); do
        count=$((count + 1))
        showStatus "sleeping" "...${count}s of ${limit}s"
        sleep 1
    done
    return 0
}

# define Signal-Handler
function handler() {
    if [ -z "$killed" ]; then
        killed=1
        touch "${logdir}/${base}/syntaxerror"
    else
        ${doSudo} killall tcpdump 2>/dev/null
        moveLogs
        moveBchannelLogs
        startPost 1 0 0
        deleteSyslogConfig
        exit 2
    fi
}

# Signal-Handler "handler()" mit Signal SIGUSR1 verbinden
trap handler SIGINT

usage() {
    echo >&2 "Usage: $0 [-t tests] [-n number to dial] [-b basenumber to dial] [-B basenumber2 to dial] [-x sender_ip] [-X receiver_ip] [-i dut_ip] [-p senderPort] [-P receiverPort] [-m ip-mode] [-d] [-t] [-a]"
    echo >&2 "ip-mode is one of the following:"
    echo >&2 "  - u1: UDP with one socket (default),"
    echo >&2 "  - un: UDP with one socket per call,"
    echo >&2 "  - ui: UDP with one socket per IP address The IP addresses must be defined in the injection tests."
    echo >&2 "  - t1: TCP with one socket,"
    echo >&2 "  - tn: TCP with one socket per call,"
    echo >&2 "  - l1: TLS with one socket,"
    echo >&2 "  - ln: TLS with one socket per call"
    echo >&2 " "
    echo >&2 "-a automatic don't wait for keys"
    echo >&2 "-r resume tests, continue all with untested"
    echo >&2 "-d call less to show sip messages"
    echo >&2 "-t direct testing don't call receiver.xml files"
    echo >&2 "-L logdir"
    echo >&2 "-j json"
    echo >&2 "-s no Test status"
    exit 1
}

while getopts "ab:B:df:i:jL:m:n:p:P:srwt:x:X:?h" o; do
    case "$o" in
    a) automatic=1 ;;
    b) basenumber="$OPTARG" ;;
    B) basenumber2="$OPTARG" ;;
    n) dest="$OPTARG" ;;
    i) dut_ip="$OPTARG" ;;
    j) json=1 ;;
    m) mode="$OPTARG" ;;
    L) logdir="$OPTARG" ;;
    d) details=1 ;;
    p) senderPort="$OPTARG" ;;
    P) receiverPort="$OPTARG" ;;
    r) resume="true" ;;
    s) noTestStatus="true" ;;
    t) tests="$OPTARG" ;;
    T) direct_testing=1 ;;
    x) sender_ip="$OPTARG" ;;
    X) receiver_ip="$OPTARG" ;;
    h) usage ;;
    ?) usage ;;
    [?]) usage ;;
    esac
done
shift $((OPTIND - 1))

if [ -z "$logdir" ]; then
    logdir=$(pwd)/logs
fi
mkdir -p "${logdir}"

if [ -z "$basenumber" ]; then
    basenumber=
fi

if [ -z "$basenumber2" ]; then
    basenumber2=
fi

if [ -z "$tests" ]; then
    tests=$(find . -regextype posix-extended -maxdepth 1 -regex '^.*/[0-9]*$' -type d -printf '%f\n' | sort)
    if [ "$resume" == "true" ]; then
        resumefiles=""
        for myfile in $(echo $tests); do
            if [ ! -e "${myfile}/ok" -a ! -e "${myfile}/ok" ]; then
                resumefiles=$resumefiles" "$myfile
            fi
        done
        tests=$resumefiles
        if [ -z "$tests" ]; then echo "There are no tests to resume" && exit 4; fi
    fi
fi

if [ -z "$dest" ]; then
    dest=1234
fi

if [ -z "$dut_ip" ]; then
    dut_ip="127.0.0.1"
fi

if [ -z "$sender_ip" ]; then
    sender_ip="127.0.1.1"
fi

if [ -z "$receiver_ip" ]; then
    receiver_ip="127.0.2.1"
fi

if [ -z "$mode" ]; then
    mode="u1"
fi

if [ -z "$senderPort" ]; then
    senderPort="6060"
fi

if [ -z "$receiverPort" ]; then
    receiverPort="7060"
fi

timeout=30
ignoreReceiver=0

#saveConfig
basenumber_save=$basenumber
basenumber2_save=$basenumber2
dest_save=$dest
mode_save=$mode
senderPort_save=$senderPort
receiverPort_save=$receiverPort
sender_ip_save=$sender_ip
receiver_ip_save=$receiver_ip
timeout_save=$timeout
ignoreReceiver_save=$ignoreReceiver

overallTestState=0
testsStartTime=$(date +%s)

xunitxml=${logdir}/junit.xml
echoJsonHeader
writeXunitHeader

numTests=0
numFailures=0

${SCRIPT_DIR}/dnsmasq/enable

./setOption /data/config.lua ushort_OptionsInterval 0
waitForTimeout 60

for i in $tests; do
    preSleep=0
    postSleep=0
    prePostSleep=0

    if test -e "$i"; then
        numTests=$((numTests + 1))

        unset receiverPid
        base=${i}

        mkdir -p "${logdir}/${base}/" 2>/dev/null
        chmod uag+rw "${logdir}/${base}"

        rm -rf "${logdir}/${base}"/*
        cp -r "${base}"/* "${logdir}/${base}"

        #exec &>"${logdir}/${base}/test.log"

        killall -9 omgsipp 2>/dev/null
        killall -9 dumppcap 2>/dev/null

        setScenarioName

        writeSyslogConfig
        clearBchannelLogs

        if [ -e "${logdir}/${base}/config" ]; then
            . "${logdir}/${base}/config"
        fi

        cmd=$(grep "<\!-- +" "${logdir}/${base}/sender1.xml" | sed -e "s/.*+\(.*\)+.*/\1/")
        echo ${cmd} >command.tmp
        . ./command.tmp
        rm command.tmp

        question=$(grep "<\!-- #" "${logdir}/${base}/sender1.xml" | sed -e "s/.*#\(.*\)#.*/\1/")
        if [ ! -z "$question" ]; then
            if [ -z "$automatic" ]; then
                overwrite $question
                read dummy
                #read -n1 -p "Hit any key to continue. "
            fi
        fi

        cp /data/config.lua ${logdir}/${base}/config.org.lua

        startDnsmasq "${logdir}/${base}"

        testStartTime=$(date +%s)

        showStatus "testing" "..."

        startPre

        cp /data/config.lua ${logdir}/${base}/config.test.lua

        if [ ! -z "$doRestart" ]; then
            showStatus "restarting DUT" "..."
            omgrestart >/dev/null 2>/dev/null
        fi
        waitForTimeout $preSleep

        ${doSudo} bash -c "tcpdump -p -n -i any -w \"${logdir}/${base}/capture.cap\" -s 0 'not port 514 and not port 22' >/dev/null 2>/dev/null &"

        subTestCount=$(find "${logdir}/${base}/" -regextype posix-extended -maxdepth 1 -regex '^.*/sender.*[.]xml' -type f -printf '%f\n' | wc -l)
        for j in $(seq 1 ${subTestCount}); do
            if [ -z "$direct_testing" ]; then
                if test -e "${logdir}/${base}/receiver${j}.xml"; then
                    # echo "====================== start receiving part (${logdir}/${base}/receiver${j}.xml) ==================="

                    parameter=$(grep "<\!-- =" ${logdir}/${base}/receiver${j}.xml | sed -e "s/.*[=]\(.*\)[=].*/\1/")
                    if [ "$parameter" = "basenumber" ]; then
                        nrToDial=$basenumber
                    elif [ "$parameter" = "basenumber2" ]; then
                        nrToDial=$basenumber2
                    else
                        nrToDial=$dest
                    fi

                    mParameter="-m 1"
                    if [ "$ignoreReceiver" == "1" ]; then
                        mParameter=""
                    fi

                    if grep -q play_pcap_audio ${logdir}/${base}/receiver${j}.xml; then
                        unset echopar
                    elif grep -q rtp_stream ${logdir}/${base}/receiver${j}.xml; then
                        unset echopar
                    else
                        echopar="-rtp_echo"
                    fi

                    cd "${logdir}/${base}"
                    echo "${doSudo} omgsipp $dut_ip -skip_rlimit -sf receiver${j}.xml -aa -t $mode -i $receiver_ip -mi $receiver_ip -mp 5000 -p $receiverPort \
                        -trace_msg -trace_shortmsg -trace_logs -trace_err -pause_msg_ign ${mParameter} -users 1 \
                        -s $nrToDial $echopar" >receiver.sipp.log
                    ${doSudo} omgsipp $dut_ip -skip_rlimit -sf receiver${j}.xml -aa -t $mode -i $receiver_ip -mi $receiver_ip -mp 5000 -p $receiverPort \
                        -trace_msg -trace_shortmsg -trace_logs -trace_err -pause_msg_ign ${mParameter} -users 1 \
                        -s $nrToDial $echopar >>"receiver.sipp.log" 2>&1 &
                    receiverPid=$!
                    cd - 2>/dev/null >/dev/null
                fi
            fi

            parameter=$(grep "<\!-- =" "${logdir}/${base}/receiver${j}.xml" | sed -e "s/.*[=]\(.*\)[=].*/\1/")
            if [ "$parameter" = "basenumber" ]; then
                nrToDial=$basenumber
            elif [ "$parameter" = "basenumber2" ]; then
                nrToDial=$basenumber2
            else
                nrToDial=$dest
            fi

            if grep -q play_pcap_audio "${logdir}/${base}/sender${j}.xml"; then
                unset echopar
            elif grep -q rtp_stream ${logdir}/${base}/sender${j}.xml; then
                unset echopar
            else
                echopar="-rtp_echo"
            fi

            cd "${logdir}/${base}"
            echo "${doSudo} omgsipp $dut_ip -skip_rlimit -sf sender${j}.xml -aa -t $mode -i $sender_ip -mp 6000 -mi $sender_ip -p $senderPort \
                -trace_msg -trace_shortmsg -trace_logs -trace_err  -pause_msg_ign -m 1 \
                -users 1 -s $nrToDial $echopar" >sender.sipp.log
            ${doSudo} omgsipp $dut_ip -skip_rlimit -sf sender${j}.xml -aa -t $mode -i $sender_ip -mp 6000 -mi $sender_ip -p $senderPort \
                -trace_msg -trace_shortmsg -trace_logs -trace_err -pause_msg_ign -m 1 \
                -users 1 -s $nrToDial $echopar >>sender.sipp.log 2>&1 &
            senderPid=$!
            #retval=$?
            cd - 2>/dev/null >/dev/null

            #echo "wait for: $senderPid $receiverPid ${timeout}s"

            if [ "$ignoreReceiver" == "1" ]; then
                waitForProcessesWithTimeout $j $timeout $senderPid
                #echo "$processList"

                if [ ! -z $senderPid ]; then
                    killProcess $senderPid
                    wait $senderPid >/dev/null 2>/dev/null
                    retval=$?
                fi

                if [ ! -z $receiverPid ]; then
                    killProcess $receiverPid
                    wait $receiverPid >/dev/null 2>/dev/null
                    receiverRetval=0
                fi
            else
                waitForProcessesWithTimeout $j $timeout $senderPid $receiverPid
                #echo "$processList"

                if [ ! -z $senderPid ]; then
                    killProcess $senderPid
                    wait $senderPid >/dev/null 2>/dev/null
                    retval=$?
                fi

                if [ ! -z $receiverPid ]; then
                    killProcess $receiverPid
                    wait $receiverPid >/dev/null 2>/dev/null
                    receiverRetval=$?
                fi
            fi
        done
        ${doSudo} killall tcpdump 2>/dev/null

        moveLogs
        moveBchannelLogs

        waitForTimeout $prePostSleep
        if [ "$processList" == "" ]; then
            startPost 0 $retval $receiverRetval
        else
            startPost 1 $retval $receiverRetval
        fi

        testStopTime=$(date +%s)
        testTimeDelta=$((testStopTime - testStartTime))

        stopDnsmasq

        # enable default DNS settings
        ${SCRIPT_DIR}/dnsmasq/enable

        if [ ! -z "$doRestart" ]; then
            showStatus "restarting DUT" "..."
            omgrestart >/dev/null 2>/dev/null
        fi
        waitForTimeout $postSleep

        if [ $retval == 0 ]; then
            if [ -e "${logdir}/${base}/syntaxerror" ]; then
                showFailedStatus 500 "failed with syntax error"
                numFailures=$((numFailures + 1))
            else
                if [ $postRetval == 0 ]; then
                    showPassedStatus 200 "ok"
                else
                    showFailedStatus $postRetval "failed in post"
                    numFailures=$((numFailures + 1))
                fi
            fi
        else
            if [ "$processList" == "" ]; then
                showFailedStatus $retval "failed"
            else
                showFailedStatus 503 "failed (timeout)"
            fi
            numFailures=$((numFailures + 1))
        fi

        if [ ! -z "$details" ]; then
            echo press return to continue ...
            read dummy
            less "${logdir}/${logdir}/${base}/messages"
        fi
    else
        echo $i did not exist
    fi

    cp ${logdir}/${base}/config.org.lua /data/config.lua

    #restoreConfig
    basenumber=$basenumber_save
    basenumber2=$basenumber2_save
    dest=$dest_save
    mode=$mode_save
    senderPort=$senderPort_save
    receiverPort=$receiverPort_save
    sender_ip=$sender_ip_save
    receiver_ip=$receiver_ip_save
    timeout=$timeout_save
    ignoreReceiver=$ignoreReceiver_save
    preSleep=0
    postSleep=0
    prePostSleep=0
    unset doRestart
done

testsStopTime=$(date +%s)
testsTimeDelta=$((testsStopTime - testsStartTime))

echoJsonFooter
writeXunitFooter ${testsTimeDelta} ${numTests} ${numFailures}
deleteSyslogConfig

touch /data/config.cfg

exit $overallTestState
