#!/bin/bash

## Copyright (C) 2014 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## Write all informational messages to stderr.
## Write date to stdout.

## exit codes:
## 0: show Tor consensus time middle range or minimum time
## 1: error
## 2: show Tor certificate lifetime
## 3: Could not determine any time using Tor from consensus or certificate life time.
## 4: Setting time using anondate either not possible or not required.

## If tor@default is stopped.
# sudo anondate-tester
#
# cmd_item: anondate --has-consensus
# output:
# exit_code: 4
# ----------
# cmd_item: anondate --current-time-in-valid-range
# sed: can't read /var/lib/tor/cached-microdesc-consensus: No such file or directory
# output:
# exit_code: 1
# ----------
# cmd_item: anondate --show-valid-after
# sed: can't read /var/lib/tor/cached-microdesc-consensus: No such file or directory
# output:
# exit_code: 1
# ----------
# cmd_item: anondate --show-valid-until
# sed: can't read /var/lib/tor/cached-microdesc-consensus: No such file or directory
# output:
# exit_code: 1
# ----------
# cmd_item: anondate --show-middle-range
# sed: can't read /var/lib/tor/cached-microdesc-consensus: No such file or directory
# output:
# exit_code: 1
# ----------
# cmd_item: anondate --tor-cert-lifetime-invalid
# output:
# exit_code: 3
# ----------
# cmd_item: anondate --tor-cert-valid-after
# output:
# exit_code: 3
# ----------

#set -x
set -e
set -o pipefail

output_cmd() {
   msg="$0: $@"
   echo "______ $msg" >&2
}

exit_handler() {
   if [ "$exit_code" = "" ]; then
      meaning="Unexpected error (1). No exit code set yet. Setting exit_code to 1."
      exit_code=1
   elif [ "$exit_code" = "0" ]; then
      meaning="Showed Tor consensus time middle range or minimum time."
   elif [ "$exit_code" = "1" ]; then
      meaning="error"
   elif [ "$exit_code" = "2" ]; then
      meaning="Showed Tor certificate life time."
   elif [ "$exit_code" = "3" ]; then
      meaning="Could not determine any time using Tor from consensus or certificate life time."
   elif [ "$exit_code" = "4" ]; then
      meaning="Setting time using anondate either not possible or not required."
   else
      meaning="Unexpected error (2)."
   fi

   output_cmd "### END: ### Exiting with exit_code '$exit_code' indicating '$meaning'."
   exit "$exit_code"
}

minimum_time_check() {
   time_result_unixtime="$(date --utc --date="$1" "+%s")"

   if ! minimum-time-check "$time_result_unixtime" ; then
      output_cmd "INFO: minimum-time-check determined the time_result to be obsolete, ok."
      output_cmd "debug: command: minimum-time-check: '$time_result_unixtime'"
      output_cmd "debug: time_result: '$time_result'"
      output_cmd "debug: time_result_unixtime: '$time_result_unixtime'"
      output_cmd "debug: earlier than"
      output_cmd "debug: minimum_unixtime_show_human_readable: '$minimum_unixtime_show_human_readable'"
      output_cmd "debug: minimum_unixtime_show_unixtime: '$minimum_unixtime_show_unixtime'"
      return 1
   fi
   output_cmd "INFO: time_result later than minimum-unixtime-show, ok."
}

trap exit_handler EXIT

who_ami="$(whoami)"

if [ "$who_ami" = "sdwdate" ]; then
   anondate_state_folder=/run/sdwdate
elif [ "$(id -u)" = "0" ]; then
   anondate_state_folder=/run/anondate
else
   anondate_state_folder=/run/anondate
   echo "ERROR: Must run as root."
   exit_code=112
   exit 112
fi


output_cmd "### START: ### $0"

true "INFO: who_ami: '$who_ami'"
true "INFO: anondate_state_folder: '$anondate_state_folder'"

[ -n "$LC_TIME" ] || export LC_TIME=C
[ -n "$TZ" ] || export TZ=UTC

## capture stdout
minimum_unixtime_show_unixtime="$(minimum-unixtime-show 2>/dev/null)"
## capture stderr
minimum_unixtime_show_human_readable="$(minimum-unixtime-show 2>&1 > /dev/null)"

anondate_tor_certificate_lifetime_set_file="${anondate_state_folder}/tor_certificate_lifetime_set"

source /usr/libexec/helper-scripts/tor_bootstrap_check.bsh
check_tor_bootstrap_helper

if [ "$tor_bootstrap_percent" = "100" ]; then
   output_cmd "INFO: 100% Tor bootstrap, ok."
   if tor_circuit_established_check="$(/usr/bin/tor-circuit-established-check)" ; then
      output_cmd "INFO: tor-circuit-established-check, ok."
      if [ "$tor_circuit_established_check" = "1" ]; then
         output_cmd "INFO: Tor circuit already established, ok."
      else
         output_cmd "WARNING: Tor circuit not yet established."
      fi
   else
      output_cmd "WARNING: tor-circuit-established-check failed."
   fi
else
   output_cmd "WARNING: Tor bootstrap not done."
fi

## TODO: Tor cert valid after must be after Tor consensus middle range to avoid using stale Tor consensus

output_cmd "INFO: Attempting to determine Tor consensus time middle range..."

## tor_bootstrap_percent might not be completed yet (not be be 100% yet), but a Tor consensus might have
## been already downloaded earlier.
##
## If Tor consensus has not been downloaded yet, '$vstart' will be empty.
## Use '&>/dev/null' to suppress expected error message:
#'ERROR: Variable 'vstart' is empty or contains only whitespace/newlines.'
##
## Checking if a Tor consensus has already been downloaded is avoided because this needs access to '$TOR_DIR' '/var/lib/tor'.
#if anondate --has-consensus ; then
#output_cmd "INFO: has Tor consensus, ok."
##
## Therefore use 'anondate --show-middle-range' to check if a Tor consensus is already available.
if time_result="$(anondate --show-middle-range 2>/dev/null)" ; then
   valid_after="$(anondate --show-valid-after)"
   ## example valid_after:
   ## 2021-01-21 15:00:00
   valid_until="$(anondate --show-valid-until)"
   ## example valid_until:
   ## 2021-01-21 18:00:00

   output_cmd "INFO: Tor consensus time middle range could be determined, ok."

   range_info="valid_after: '$valid_after' | middle_range: '$time_result' | valid_until: '$valid_until'"

   if [ "$time_result" = "" ]; then
      output_cmd "WARNING: Tor consensus time middle range result empty."
   elif anondate --current-time-in-valid-range ; then
      output_cmd "INFO: Local system time is already within Tor consensus time valid range, ok."
      output_cmd "INFO: $range_info"
      output_cmd "INFO: Tor consensus time middle_range: '$time_result'"
      if minimum_time_check "$time_result" ; then
         output_cmd "INFO: minimum-time-check determined Tor consensus time middle range to be valid (A), ok."
         ## Deliberately not doing 'echo "$time_result"' here.
         ## - Since local system time is already within valid range there is no need to fix the clock.
         ## - Avoid duplicate echo output.
         #echo "$time_result"
         exit_code=4
         ## Deliberately not 'exit' here.
         ## Tor consensus might be outdated.
         ## Permit script to flow through 'anondate --tor-cert-lifetime-valid' part.
      else
         output_cmd "INFO: minimum-time-check determined Tor consensus time middle range to be obsolete (A), ok."
      fi
      output_cmd "INFO: Do not show Tor consensus time middle range as result, ok."
   else
      output_cmd "WARNING: local system time is NOT within Tor consensus time valid time range. ($range_info)"
      if minimum_time_check "$time_result" ; then
         output_cmd "INFO: minimum-time-check determined Tor consensus time middle range to be valid (B), ok."
         output_cmd "INFO: Showing Tor consensus time middle range..."
         output_cmd "INFO: Tor consensus time middle_range: '$time_result'"
         echo "$time_result"
         exit_code=0
         exit 0
      else
         output_cmd "INFO: Tor consensus time middle_range: '$time_result'"
         output_cmd "INFO: minimum-time-check determined Tor consensus time middle range to be obsolete (B), ok."
         output_cmd "INFO: Do not show Tor consensus time middle range as result, ok."
      fi
   fi
else
   output_cmd "WARNING: Could not determine Tor consensus time middle range."
fi

# else
#    output_cmd "WARNING: has no Tor consensus yet."
# fi

output_cmd "INFO: Attempting to determine Tor certificate lifetime..."
if anondate --tor-cert-lifetime-valid ; then
   output_cmd "INFO: Tor certificate lifetime valid, ok."
else
   output_cmd "WARNING: Tor certificate lifetime invalid according to Tor log. This information might be outdated and not matter."
   if time_result="$(anondate --tor-cert-valid-after)" ; then
      output_cmd "INFO: Tor certificate life date: '$time_result'"
      time_result_unixtime="$(date --utc --date="$time_result" "+%s")"
      current_unixtime="$(date --utc "+%s")"
      if [ "$current_unixtime" -ge "$time_result_unixtime" ]; then
         output_cmd "INFO: System clock is LATER than Tor certificate life date, ok."
      else
         output_cmd "INFO: System clock is EARLIER than Tor certificate life date."
      fi
      if minimum_time_check "$time_result" ; then
         output_cmd "INFO: minimum-time-check determined Tor certificate lifetime to be valid, ok."
         output_cmd "INFO: Showing Tor certificate lifetime as result..."
         output_cmd "INFO: Tor certificate lifetime: '$time_result'"
         echo "$time_result"
         exit_code=2
         exit 2
      else
         output_cmd "INFO: minimum-time-check determined Tor certificate lifetime to be obsolete, ok."
         output_cmd "INFO: Do not show Tor certificate lifetime as result, ok."
      fi
   else
      output_cmd "WARNING: Could not determine Tor certificate lifetime."
   fi
fi

## Tor consensus time possibly later than system clock but minimum time later than Tor consensus time.
output_cmd "INFO: Could not determine a time later than minimum time from either Tor consensus time or Tor certificate lifetime, ok."
output_cmd "INFO: Showing minimum time instead as result..."
## anondate-set would not set the time backwards anyhow, therefore safe to show minimum time here.
output_cmd "INFO: minimum time: '$minimum_unixtime_show_human_readable'"
echo "$minimum_unixtime_show_human_readable"
exit_code=0
exit 0
