# this file is a library sourced from recipes/*

result_path=$(pwd)
cd $(dirname "$0")/../
script_path=$(pwd)
cd "${result_path}"

result_logs() {
  # $1 = test name
  # $2 = status: "ok" / "failed" / "configuration failed" / "expected error"
  #              "skipped" / "ncat (nc) failed" / "shouldn't work"
  # $3 = file name: "stunnel" / "error"

  if [ "$2" = "expected error" ]
    then # expected error - it's ok
      printf "%-35s\t%s\n" "test $1" "ok"
     else
      printf "%-35s\t%s\n" "test $1" "$2"
     fi
  if [ "$2" != "ok" ] && [ "$2" != "ncat (nc) failed" ]
    then
      printf "%-35s\t%s\n" "test $1" "$2" >> "results.log"
    fi
  if [ "$2" = "failed" ] || [ "$2" = "configuration failed" ] || [ "$2" = "shouldn't work" ]
    then # file with stunnel error logs
      printf "%-35s\t%s\n" "error logs" "logs/$1.log"
      cat "$3.log" > "$1.log"
    else
      cat "temp.log" >> "results.log" 2>> "stderr_nc.log"
    fi
  return 0
}

exit_logs() {
  # $1 = test name
  # $2 = status

  case "$2" in
    "ok") result_logs "$1" "ok" "UNUSED PATTERN";;
    "failed") result_logs "$1" "failed" "stunnel";;
    "configuration failed") result_logs "$1" "configuration failed" "error";;
    "expected error") result_logs "$1" "expected error" "UNUSED PATTERN";;
    "skipped") result_logs "$1" "skipped" "error";;
    "ncat (nc) failed") result_logs "$1" "failed" "stunnel";;
    "shouldn't work") result_logs "$1" "shouldn't work" "stunnel";;
  esac
  return 0
}

clean_logs() {
  rm -f "stunnel.log"
  rm -f "temp.log"
  rm -f "error.log"
  rm -f "stderr_nc.log"
  rm -f "stunnel.conf"
  return 0
}

waiting_for() {
  # waiting for strings ($2 or $3 or $4) to appear in the file $1.log

  mkfifo "fifo"
  (cat "$1.log"; tail -f "$1.log") > "fifo" 2>> "stderr_nc.log" &
  pid_tail=$!
  (sleep 3; echo "TIMEOUT") > "fifo" &
  pid_timeout=$!
  grep -q -e "$2" -e "$3" -e "$4" -e "TIMEOUT" "fifo"
  pid_children=$(ps -o pid,ppid | \
    awk -v ppid1="${pid_tail}" -v ppid2="${pid_timeout}" \
      '{if ($2==ppid1 || $2==ppid2) print $1}')
  kill ${pid_tail} ${pid_timeout} ${pid_children} 2>> "stderr_nc.log"
  wait ${pid_tail} ${pid_timeout} 2>> "stderr_nc.log"
  rm -f "fifo"
  return 0
}

connecting_ncat() {
  # $1 = test name
  # $2 = string to send
  # $3 = netcat name: "ncat" / "nc"

  result=0
  mkfifo "nodata"
  printf "\n%s\n" "test $1" > "stderr_nc.log"
  cat "nodata" | "$3" -l -p "$http2" -vvv >"temp.log" 2>> "stderr_nc.log" &
  pid_nc=$!
  waiting_for "stderr_nc" "Listening" "listening" "QUITTING"
  if grep -q "istening" "stderr_nc.log"
    then # Listening or listening
      if [ "$3" = "ncat" ]
        then
          printf "%-35s\t%s\n" "test $1" "$2" | "$3" 127.0.0.1 "$http1" -vv 2>> "stderr_nc.log"
        else
          printf "%-35s\t%s\n" "test $1" "$2" | "$3" 127.0.0.1 "$http1" -vv 2>> "stderr_nc.log" &
        fi
    else # ncat (nc) failed
      result=1
    fi
  if [  "$3" = ncat ]
    then
      waiting_for "stderr_nc" "Closing" "Connection reset by peer" "UNUSED PATTERN"
    else
      waiting_for "stderr_nc" "accepted" "from localhost" "Connection reset by peer"
  fi
  kill -TERM ${pid_nc} 2>> "stderr_nc.log"
  cat "stderr_nc.log" >> "stderr.log"
  echo "somedata" > "nodata"
  rm -f "nodata"
  return $result
}

killing_stunnel() {
  waiting_for "$1" "Service .* finished" "Sent socket write shutdown" "UNUSED PATTERN"
  kill -TERM $(tail "stunnel.pid") 2>> "stderr.log"
  waiting_for "stunnel" "removing pid file" "UNUSED PATTERN" "UNUSED PATTERN"
  return 0
}

reload_stunnel() {
  waiting_for "stunnel" "stunnel.pid" "UNUSED PATTERN" "UNUSED PATTERN"
  kill -HUP $(tail "stunnel.pid") 2>> "stderr.log"
  waiting_for "stunnel" "127.0.0.1:${http1}" "UNUSED PATTERN" "UNUSED PATTERN"
  return 0
}

expected_success() {
  # expects to send the s using stunnel
  # $1 = test name
  # $2 = netcat name: "ncat" / "nc"

  result=0
  if [ ! -s "error.log" ]
    then
      if connecting_ncat "$1" "success" "$2"
        then
          if grep -q "test $1.*success" "temp.log"
            then
              exit_code="ok"
            else # test failed
              exit_code="failed"
              result=1
            fi
        else # ncat (nc) failed
          exit_code="ncat (nc) failed"
          result=1
        fi
      killing_stunnel stunnel
    else # configuration failed
      exit_code="configuration failed"
      result=1
    fi
  exit_logs "$1" "$exit_code"
  return $result
}

expected_failure() {
  # $1 = test name
  # $2 = netcat name: "ncat" / "nc"

  result=0
  if [ ! -s "error.log" ]
    then
      if connecting_ncat "$1" "shouldn't work" "$2"
        then
          if grep -q "test $1.*shouldn't work" "temp.log"
            then # ops...stunnel works
              exit_code="shouldn't work"
              result=1
            else
              exit_code="expected error"
            fi
        else # ncat (nc) failed
          exit_code="ncat (nc) failed"
          result=1
        fi
      killing_stunnel stunnel
    else # configuration failed, but it is ok
      exit_code="expected error"
    fi
  exit_logs "$1" "$exit_code"
  return $result
}

execute_program() {
  # $1 = test name
  # $2 = netcat name: "ncat" / "nc"

  result=0
  mkfifo "nodata"
  if [ ! -s "error.log" ]
    then
      cat "nodata" | "$2" 127.0.0.1 "$http1" -vv > "temp.log" 2>>"stderr.log" &
      pid_nce=$!
      killing_stunnel stunnel
      kill -TERM ${pid_nce} 2>> "stderr.log"
      echo "somedata" > "nodata" 2>> "stderr.log"
      rm -f "nodata"
      if grep -q "test $1.*success" "temp.log"
        then
          if grep -q "$1_error" "temp.log"
            then # only for redirect tests
              exit_code="failed"
              result=1
            else
              exit_code="ok"
          fi
        else # test failed
          exit_code="failed"
          result=1
        fi
    else # configuration failed
      exit_code="configuration failed"
      result=1
    fi
  exit_logs "$1" "$exit_code"
  return $result
}

loop_prio() {
  # $1 = test name
  # $2 = netcat name: "ncat" / "nc"

  result=0
  i=1
  max=12
  start $i 2> "error.log"
  if [ ! -s "error.log" ]
    then
      waiting_for "stunnel" "Created pid file" "UNUSED PATTERN" "UNUSED PATTERN"
      mv "stunnel.log" "stunnel_0.log"
      kill -USR1 $(tail "stunnel.pid") 2>> "stderr.log"
      while [ $i -le $max ] && [ $result -eq 0 ]
        do
          if connecting_ncat "$1" "success" "$2"
            then
              waiting_for "stunnel" "Service .* finished" "Sent socket write shutdown" "UNUSED PATTERN"
              if grep -q "test $1.*success" "temp.log"
                then
                  if [ $1 = "037_failover_prio1" ]
                    then
                      serv="server_2\] accepted connection"
                    else
                      serv="server_1\] accepted connection"
                    fi
		  if ! grep -q "$serv" "stunnel.log"
		    then # second server doesn't accept any client
		      if [ $i -eq $max ]
			then # last successed turn of the loop
			  exit_code="ok"
			fi
		    else # error - second server accepts a client
                      exit_code="failed"
		      result=1
		    fi
                else # stunnel doesn't work
                  exit_code="failed"
                  result=1
                fi
            else # ncat (nc) failed
              exit_code="ncat (nc) failed"
              result=1
            fi
          waiting_for "stunnel" "Service .* finished" "Sent socket write shutdown" "UNUSED PATTERN"
          mv "stunnel.log" "stunnel_$i.log"
          kill -USR1 $(tail "stunnel.pid") 2>> "stderr.log"
          i=$((i + 1))
        done
      cat "stunnel_0.log" > "stunnel_all.log"
      rm -f "stunnel_0.log"
      j=1
      while [ $j -lt $i ]
        do
          printf "%s\n" "connection $j" >> "stunnel_all.log"
          cat "stunnel_$j.log" >> "stunnel_all.log"
          rm -f "stunnel_$j.log"
          j=$((j + 1))
        done
      killing_stunnel stunnel_all
      cat "stunnel.log" >> "stunnel_all.log"
      cat "stunnel_all.log" > "stunnel.log"
      rm -f "stunnel_all.log"
    else # configuration failed
      exit_code="configuration failed"
      result=1
    fi
  exit_logs "$1" "$exit_code"
  return $result
}

loop_rr() {
  # $1 = test name
  # $2 = netcat name: "ncat" / "nc"

  result=0
  i=1
  max=3
  first=0
  second=0
  third=0
  start $i 2> "error.log"
  if [ ! -s "error.log" ]
    then
      waiting_for "stunnel" "Created pid file" "UNUSED PATTERN" "UNUSED PATTERN"
      mv "stunnel.log" "stunnel_0.log"
      kill -USR1 $(tail "stunnel.pid") 2>> "stderr.log"
      while [ $i -le $max ] && [ $result -eq 0 ]
        do
          if connecting_ncat "$1" "success" "$2"
            then
              waiting_for "stunnel" "Service .* finished" "Sent socket write shutdown" "UNUSED PATTERN"
              if ! grep -q "test $1.*success" "temp.log"
                then # stunnel doesn't work
                  exit_code="failed"
                  result=1
                fi
            else # ncat (nc) failed
              exit_code="ncat (nc) failed"
              result=1
            fi
          waiting_for "stunnel" "Service .* finished" "Sent socket write shutdown" "UNUSED PATTERN"
          mv "stunnel.log" "stunnel_$i.log"
          kill -USR1 $(tail "stunnel.pid") 2>> "stderr.log"
          i=$((i + 1))
        done
      cat "stunnel_0.log" > "stunnel_all.log"
      rm -f "stunnel_0.log"
      j=1
      while [ $j -lt $i ]
        do
          printf "%s\n" "connection $j" >> "stunnel_all.log"
          cat "stunnel_$j.log" >> "stunnel_all.log"
          rm -f "stunnel_$j.log"
          j=$((j + 1))
        done
      killing_stunnel stunnel_all
      cat "stunnel.log" >> "stunnel_all.log"
      cat "stunnel_all.log" > "stunnel.log"
      rm -f "stunnel_all.log"
      first=$(grep -c "server_1\] accepted connection" "stunnel.log")
      second=$(grep -c "server_2\] accepted connection" "stunnel.log")
      third=$(grep -c "server_3\] accepted connection" "stunnel.log")
    else # configuration failed
      exit_code="configuration failed"
      result=1
    fi
    if [ $result -eq 0 ]
      then
        product=$((first * second * third))
        if [ $product -ne 0 ]
          then # round robin
            printf "%-35s\t%s\n" "test $1: $first x $second x $third" "success" > "temp.log"
            exit_code="ok"
          else
            printf "%-35s\t%s\n" "test $1: $first x $second x $third" "failed" > "temp.log"
            exit_code="failed"
            result=1
          fi
      fi
  exit_logs "$1" "$exit_code"
  return $result
}

test_log_for() {
  # $1 = test name
  # $2 = function name
  # $3 = netcat name: "ncat" / "nc"

  case "$2" in
    "success") expected_success "$1" "$3";;
    "failure") expected_failure "$1" "$3";;
    "execute") execute_program "$1" "$3";;
    "prio") loop_prio "$1" "$3";;
    "rr") loop_rr "$1" "$3";;
  esac
  result=$?
  clean_logs
  return $result
}

set_port() {
  port=$((port+1))
  while netstat -an 2>> "stderr.log" | grep $port | grep -q LISTEN
    do
      port=$((port+1))
    done
  return 0
}

check_ports() {
  port=8079
  set_port $port
  http1=$port
  set_port $port
  http2=$port
  set_port $port
  http3=$port

  port=4432
  set_port $port
  https=$port
  set_port $port
  https2=$port
  set_port $port
  https3=$port

  printf "\n%s\n" "test $1" >> "stderr.log"
  printf "%s\n" "ports: $http1 $http2 $http3 $https $https2 $https3" >> "stderr.log"
  return 0
}
