#! /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
