#! /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="/usr/bin/sudo"

export PATH=$PATH:${toolsPath}

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 ! 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
}

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}/sender.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 waitTimeout {
  local limit=${@:1:1}
  local pids=${@:2}
  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"  "... ${count}s of ${limit}s"
      sleep 1
    else
      showStatus "testing"  "timeout ${limit} reached"
      processList=${pids}
      return 1
    fi
  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] [-l local_ip] [-i ip-address] [-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:L:m:n:p:P:srwt:?h" o
  do case "$o" in
  a)   automatic=1;;
  b)   basenumber="$OPTARG";;
  B)   basenumber2="$OPTARG";;
  n)   dest="$OPTARG";;
  i)   ip="$OPTARG";;
  j)   json=1;;
  m)   mode="$OPTARG";;
  l)   local_ip="$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;;
  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=1998
fi

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

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

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

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

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

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

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

numTests=0
numFailures=0

for i in $tests; do
    if test -e "$i" ;then
        testStartTime=$(date +%s);

        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

        cmd=$(grep "<\!-- +" "${logdir}/${base}/receiver.xml" | sed -e "s/.*+\(.*\)+.*/\1/")

        echo ${cmd} > command.tmp
        . ./command.tmp
        rm command.tmp

        question=$(grep "<\!-- #" "${logdir}/${base}/receiver.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

        ignoreReceiver=0
        if [ -e "${logdir}/${base}/ignoreReceiver" ]; then
            ignoreReceiver=1
        fi

        showStatus "testing"  "..."

        startPre

        ${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 &"

        if [ -z "$direct_testing" ] ;then
            if test -e "${logdir}/${base}/receiver.xml" ;then
                # echo "====================== start receiving part (${logdir}/${base}/receiver.xml) ==================="

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

                parameter=$(grep "<\!-- =" ${logdir}/${base}/receiver.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

                cd "${logdir}/${base}"
                ${doSudo} omgsipp $ip -skip_rlimit -sf receiver.xml -aa -t $mode -i $local_ip -mi $local_ip -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

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

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

        cd "${logdir}/${base}"
        ${doSudo} omgsipp $ip -skip_rlimit -sf sender.xml -aa -t $mode -i $local_ip -mi $local_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

        timeout=30
        if [ -e "${logdir}/${base}/timeout" ]; then
            timeout=$(cat "${logdir}/${base}/timeout")
        fi
        #echo "wait for: $senderPid $receiverPid ${timeout}s"

        if [ "$ignoreReceiver" == "1" ]; then
            waitTimeout $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
            waitTimeout $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

        ${doSudo} killall tcpdump 2>/dev/null

        moveLogs
        moveBchannelLogs

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

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

        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
done

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

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

exit $overallTestState
