#!/bin/bash

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

source /usr/libexec/systemcheck/uwt_tool.bsh

systemcheck_autostart_functions+=" source_config "
systemcheck_autostart_functions+=" input_detection "
systemcheck_autostart_functions+=" root_check "
systemcheck_autostart_functions+=" msgdispatcher_init "
systemcheck_autostart_functions+=" sysmaint_state_detection "
systemcheck_autostart_functions+=" cleanup "

output_if_verbose() {
   if [ "$verbose" -ge "1" ]; then
      "$@"
   fi
}

source_config() {
   shopt -s nullglob
   local i file_perms
   for i in \
      /etc/systemcheck.d/*.conf \
      /usr/local/etc/systemcheck.d/*.conf \
      ; do
         file_perms="$(stat -c '%a' "$i" 2>/dev/null)"
         if [[ "${file_perms}" == *[2367] ]]; then
            printf '%s\n' "${FUNCNAME[0]}: ERROR: Configuration file '$i' is world-writable!"
            return 1;
         fi
         bash -n "$i"
         source "$i"
   done
}

input_detection() {
   ## Variables required by 'msgcollector' function 'msgdispatcher_init':
   ## '$GUI' and '$CLI'.

   if [ "$CLI" = "true" ] && [ "$GUI" = "true" ]; then
      CLI="true"
      GUI="true"
      input_auto="false"
      input_msg="<p>Input Detection: INPUT_AUTO=$input_auto CLI=$CLI GUI=$GUI
Manually set to --gui and --cli.</p>"
   elif [ "$CLI" = "true" ]; then
      CLI="true"
      GUI="false"
      input_auto="false"
      input_msg="<p>Input Detection: INPUT_AUTO=$input_auto CLI=$CLI GUI=$GUI
Manually set to --cli.</p>"
   elif [ "$GUI" = "true" ]; then
      CLI="false"
      GUI="true"
      input_auto="false"
      input_msg="<p>Input Detection: INPUT_AUTO=$input_auto CLI=$CLI GUI=$GUI
Manually set to --gui.</p>"
   elif [ -t "0" ]; then
      CLI="true"
      GUI="false"
      input_auto="true"
      input_msg="<p>Input Detection: INPUT_AUTO=$input_auto CLI=$CLI GUI=$GUI
stdin connected to terminal. Using cli output. Not using gui output.
Alternatively, if want to run from \
command line, but still use the graphical user interface for input, \
you could add to command line: --gui</p>"
   else
      CLI="true"
      GUI="true"
      input_auto="true"
      input_msg="<p>Input Detection: INPUT_AUTO=$input_auto CLI=$CLI GUI=$GUI
stdin not connected to terminal, probably run in graphical environment, ok.</p>"
   fi
   if [ "$CLI" = "false" ]; then
      ## Set output_cli to bash built-in 'true' so it will be ignored.
      output_cli="true"
   fi
   if [ "$GUI" = "false" ]; then
      ## Set output_x to bash built-in 'true' so it will be ignored.
      output_x="true"
   fi
}

input_notification() {
   local MSG
   MSG="$input_msg"
   if [ "$verbose" -ge "2" ]; then
      $output_x ${output_opts[@]} --messagex --typex "info" --message "$MSG"
      $output_cli ${output_opts[@]} --messagecli --typecli "info" --message "$MSG"
   fi
}

check_login_environment() {
  if [ "${PROFILE_SCRIPTS_WERE_SOURCED-}" != 'yes' ]; then
    MSG="<p>You do not appear to be running systemcheck in a 'login environment'. Most tests will likely fail. Please log in as the user you wish to run systemcheck as, then run systemcheck from there.</p>"
    $output_x ${output_opts[@]} --messagex --typex "warning" --message "$MSG"

    MSG="
You do not appear to be running systemcheck in a 'login environment'. Most
tests will likely fail. Please log in as the user you wish to run systemcheck
as, then run systemcheck from there."
    $output_cli ${output_opts[@]} --messagecli --typecli "warning" --message "$MSG"
  fi
}

show_systemcheck_header() {
   if [ "$verbose" -ge "1" ] || [ "$qubes_detected" = "true" ]; then
      local MSG="$TITLE_CLI"
      $output_cli ${output_opts[@]} --messagecli --typecli "info" --message "$MSG"
   fi
}

virtualizer_detection() {
   [ -n "$systemcheck_virt_detection_tool" ] || systemcheck_virt_detection_tool="systemd-detect-virt"

   ## Global variable systemcheck_virtualizer_detected.
   ## (Used by check_pvclock.)

   systemcheck_platform_or_virtualizer_known=false

   if ! command -v "$systemcheck_virt_detection_tool" >/dev/null 2>&1 ; then
      systemcheck_virtualizer_tool_unavailable="true"
      systemcheck_virtualizer_detected="unknown"
      return 0
   fi

   if systemcheck_virtualizer_detected="$(leaprun systemd-detect-virt 2>&1)" ; then
      true "INFO: No error reported from running $systemcheck_virt_detection_tool"

      if [ "$systemcheck_virtualizer_detected" = "xen" ]; then
         if [ "$qubes_detected" = "true" ]; then
            systemcheck_virtualizer_detected="qubes"
         fi
      fi
   else
      true "INFO: leaprun systemd-detect-virt non-zero exit code. Expected on bare metal."
      ## in case of bare metal:
      ## systemcheck_virtualizer_detected=none
   fi

   if [ "$systemcheck_virtualizer_detected" = "" ]; then
      true "INFO: Variable systemcheck_virtualizer_detected is empty, falling back to systemcheck_virtualizer_detected=unknown"
      systemcheck_virtualizer_detected="unknown"
   fi

   if [ "$systemcheck_virtualizer_detected" = "none" ]; then
      systemcheck_platform_or_virtualizer_known=true
   fi
   if [ "$systemcheck_virtualizer_detected" = "oracle" ]; then
      systemcheck_platform_or_virtualizer_known=true
   fi
   if [ "$systemcheck_virtualizer_detected" = "kvm" ]; then
      systemcheck_platform_or_virtualizer_known=true
   fi
   if [ "$systemcheck_virtualizer_detected" = "qemu" ]; then
      systemcheck_platform_or_virtualizer_known=true
   fi
   if [ "$systemcheck_virtualizer_detected" = "qubes" ]; then
      systemcheck_platform_or_virtualizer_known=true
   fi
   if [ "$systemcheck_virtualizer_detected" = "vmware" ]; then
      systemcheck_platform_or_virtualizer_known=true
   fi
}

sysmaint_state_detection() {
  local kernel_cmdline

  user_sysmaint_split_installed='false'
  booted_in_sysmaint_session='false'
  booted_with_remove_sysmaint_qubes='false'

  if package-installed-check user-sysmaint-split; then
    user_sysmaint_split_installed='true'
  fi

  kernel_cmdline=''
  if [ -f /proc/cmdline ]; then
    kernel_cmdline="$(cat -- /proc/cmdline)" || true
  elif [ -f /proc/1/cmdline ]; then
    kernel_cmdline="$(cat -- /proc/1/cmdline)" || true
  fi

  if [[ "$kernel_cmdline" =~ 'boot-role=sysmaint' ]]; then
    booted_in_sysmaint_session='true'
  fi

  if [[ "$kernel_cmdline" =~ 'remove-sysmaint-qubes' ]]; then
    booted_with_remove_sysmaint_qubes='true'
  fi
}

if_you_know_what_you_are_doing_funct() {
   ## $1 - $FUNCNAME
   echo "\
If you know what you are doing, feel free to disable this check.
Create a file <code>/etc/systemcheck.d/50_user.conf</code> and add:
<code><blockquote>systemcheck_skip_functions+=\" $1 \"</blockquote></code>"
}

systemcheck_run_function() {
   case $systemcheck_skip_functions in
   *"$@"*) echo "INFO: Skipping $@, because systemcheck_skip_functions includes it."
           return 0
           ;;
   esac
   if [ "$systemcheck_autostart_mode" = 'true' ]; then
      case $systemcheck_autostart_functions in
          *"$@"*) true;;
          *) #echo "INFO: Skipping $@, because systemcheck_autostart_mode is 'true' and systemcheck_autostart_functions does not include it."
             return 0
             ;;
      esac
   fi
   true "INFO: Running $@."
   "$@"
}

get_local_derivative_version() {
   ## fallback
   derivative_deb_package_version="Could not detect derivative debian version. (Code: 1) Please report this bug!"
   derivative_deb_package_version_detection_success="false"
   derivative_deb_package_name="(package detection failed)"
   derivative_major_release_version="(could not detect derivative_major_release_version)"

   derivative_deb_package_name="dist-general-cli"

   local dpkg_query_exit_code
   dpkg_query_exit_code="0"
   derivative_deb_package_version="$(dpkg-query --show --showformat='${Version}' "$derivative_deb_package_name" 2>/dev/null)" || { dpkg_query_exit_code="$?" ; true; };

   if [ "$dpkg_query_exit_code" = "0" ]; then
      derivative_deb_package_version_detection_success="true"
   else
      derivative_deb_package_version_detection_success="false"
      derivative_deb_package_version="Could not detect derivative derivative_deb_package_name version. (Code: 2) Probably because package $derivative_deb_package_name is not installed."
   fi

   ## Example: derivative_deb_package_version="2:6-debpackage1"

   if [ "$derivative_deb_package_version_detection_success" = "true" ]; then
      ## Remove epoch from derivative_deb_package_version.
      derivative_deb_package_version="$(echo "$derivative_deb_package_version" | awk -F ":" '{ print $2 }')"
   fi

   ## Example: derivative_deb_package_version="6-debpackage1"

   ## fallback
   if [ "$derivative_deb_package_version" = "" ]; then
      derivative_deb_package_version_detection_success="false"
      derivative_deb_package_version="Could not detect derivative $derivative_deb_package_name version. (Code: 2) Please report this bug!"
   fi

   ## fallback
   derivative_build_version="Could not read derivative Build Version File. (Code: 3) Please report this bug!"
   derivative_build_version_detection_success="false"

   local build_version_file
   if [ -f "/usr/share/whonix/build_version" ]; then
      ## Legacy.
      build_version_file="/usr/share/whonix/build_version"
   elif [ -f "/var/lib/anon-dist/build_version" ]; then
      ## Legacy.
      build_version_file="/var/lib/anon-dist/build_version"
   elif [ -f "/var/lib/dist-base-files/build_version" ]; then
      build_version_file="/var/lib/dist-base-files/build_version"
   fi

   if [ "$build_version_file" = "" ]; then
      derivative_build_version_detection_success="false"
      derivative_build_version="There is no derivative Build Version File. (Code: 4) Please report this bug!"
   else
      if [ -f "$build_version_file" ]; then
         derivative_build_version="$(cat "$build_version_file")"
         derivative_build_version_detection_success="true"
      fi
   fi

   ## fallback
   if [ "$derivative_build_version" = "" ]; then
      derivative_build_version_detection_success="false"
      derivative_build_version="Could not read derivative Build Version File. (Code: 5) Please report this bug!"
   fi

   if test -f "/etc/${PROJECT_NAME_LOWERCASE}_version" ; then
      derivative_major_release_version="$(cat "/etc/${PROJECT_NAME_LOWERCASE}_version")" || true
   fi
}

preparation() {
   ## For package 'msgcollector' function 'msgdispatcher_init'.
   [ -n "$who_ami" ] || who_ami="$(whoami)"

   ## required by virtualizer_detection
   if command -v qubesdb-read >/dev/null 2>&1 ; then
      qubes_detected="true"
   else
      qubes_detected="false"
   fi

   ## 'input_detection' must run first because 'msgdispatcher_init' depends on it.
   systemcheck_run_function input_detection
   systemcheck_run_function msgdispatcher_init
   systemcheck_run_function virtualizer_detection
   systemcheck_run_function sysmaint_state_detection

   ## 'verbose' is initialized by the main systemcheck script.

   local my_tty
   local my_tty_exit_code
   my_tty_exit_code="0"
   my_tty="$(tty)" || { my_tty_exit_code="$?" ; true; };

   if [ ! "$my_tty_exit_code" = "0" ]; then
      my_tty="none"
   fi

   ## Just in case.
   if [ "$my_tty" = "" ]; then
      my_tty="none"
   fi

   if test -f /usr/share/whonix/marker ; then
      PROJECT_HOMEPAGE="https://www.whonix.org"
      PROJECT_NAME="Whonix"
      PROJECT_NAME_LOWERCASE="whonix"
   else
      PROJECT_HOMEPAGE="https://www.kicksecure.com"
      PROJECT_NAME="Kicksecure"
      PROJECT_NAME_LOWERCASE="kicksecure"
   fi

   user_name="user"
   TEMP_DIR="$(mktemp --directory)"
   export TEMP_DIR

   ## returns: derivative_deb_package_version
   get_local_derivative_version

   ## Prepare temporary directory.
   chmod 700 "$TEMP_DIR"

   if [ -f "/usr/share/anon-gw-base-files/gateway" ]; then
      VM="Whonix-Gateway"
      vm_lower_case_short="gateway"
   elif [ -f "/usr/share/anon-ws-base-files/workstation" ]; then
      VM="Whonix-Workstation"
      vm_lower_case_short="workstation"
   else
      VM="machine"
      vm_lower_case_short="machine"
   fi

   ## sets: GATEWAY_IP
   ## sets: GATEWAY_IP6
   ## sets: gateway_control_port
   eval $(/usr/libexec/helper-scripts/settings_echo)

   ## 'uname --machine' is better than 'dpkg --print-architecture'.
   ## ARCH="x86_64"
   ## ARCH="i686"
   ARCH="$(uname --machine)"
   if [ "$ARCH" = "x86_64" ]; then
      intel_amd_64_detected=true
   else
      intel_amd_64_detected=false
   fi

   if [ "$qubes_detected" = "true" ]; then
      if test -f /usr/lib/qubes-whonix/utility_functions.sh ; then
         ## sets: PROXY_SERVER
         ## sets: PROXY_META
         source /usr/lib/qubes-whonix/utility_functions.sh
      else
         PROXY_SERVER='http://127.0.0.1:8082/'
         PROXY_META="text/html"
      fi

      ## Overwrite with || true to prevent failing just because of this.
      ## '/name' outputs for example:
      ## sys-whonix
      ## whonix
      qubes_name_of_vm="$(qubesdb-read /name)" || true

      passive_popup_title="[${qubes_name_of_vm}]"

      qubes_starter_first_part="ServiceVM"
      if [ "$qubes_name_of_vm" = "AppVM" ]; then
         qubes_starter_first_part="Domain"
      fi
      if [ "$qubes_name_of_vm" = "TemplateVM" ]; then
         qubes_starter_first_part="Template"
      fi

      ## '/qubes-vm-type' is either 'AppVM', 'ProxyVM', 'NetVM' or 'TemplateVM'
      qubes_vm_type="$(qubesdb-read /qubes-vm-type)" || true

      ## '/qubes-vm-persistence' outputs:
      ## - 'full' (TemplateVM or Standalone),
      ## - 'rw-only' (TemplateBasedVM), or
      ## - 'none' (DispVM).
      qubes_vm_persistence="$(qubesdb-read /qubes-vm-persistence)" || true

      ## '/qubes-base-template outputs for example:
      ## whonix-gw-14
      ## whonix-ws-14
      qubes_base_template="$(qubesdb-read /qubes-base-template)" || true

      if test -f /run/qubes/this-is-templatevm ; then
         qubes_title_extra=" | TemplateVM"
         update_where="this TemplateVM ('<code>$qubes_name_of_vm</code>')"
         persistent_changes_start_menu_instructions_first_part="dom0 -> Start Menu -> Template: <code>$qubes_name_of_vm</code> ->"
         start_menu_instructions_system_first_part="$persistent_changes_start_menu_instructions_first_part"
      else
         if [ "$qubes_vm_persistence" = "full" ]; then
            qubes_title_extra=" | Standalone $qubes_vm_type"
            update_where="this StandaloneVM ('<code>$qubes_name_of_vm</code>')"
            persistent_changes_start_menu_instructions_first_part="dom0 -> Start Menu -> Domain: <code>$qubes_name_of_vm</code> ->"
            start_menu_instructions_system_first_part="$persistent_changes_start_menu_instructions_first_part"
         elif [ "$qubes_vm_persistence" = "rw-only" ]; then
            qubes_title_extra=" | $qubes_base_template TemplateBased $qubes_vm_type"
            update_where="your '<code>$qubes_base_template</code>' TemplateVM"
            persistent_changes_start_menu_instructions_first_part="dom0 -> Start Menu -> Template: <code>$qubes_base_template</code> ->"
            start_menu_instructions_system_first_part="dom0 -> Start Menu -> $qubes_starter_first_part: <code>$qubes_name_of_vm</code> ->"
         elif [ "$qubes_vm_persistence" = "none" ]; then
            qubes_title_extra=" | $qubes_base_template DispVM $qubes_vm_type"
            update_where="your '<code>$qubes_base_template</code>' TemplateVM"
            persistent_changes_start_menu_instructions_first_part="dom0 -> Start Menu -> Template: <code>$qubes_base_template</code> ->"
            start_menu_instructions_system_first_part="dom0 -> Start Menu -> $qubes_starter_first_part: <code>$qubes_name_of_vm</code> ->"
         else
            qubes_title_extra=" | Unknown Qubes VM Type"
            update_where="<?> your '<code>$qubes_base_template</code>' TemplateVM </?>"
            persistent_changes_start_menu_instructions_first_part="dom0 -> Start Menu -> Template: <code>$qubes_base_template</code> ->"
            start_menu_instructions_system_first_part="dom0 -> Start Menu -> $qubes_starter_first_part: <code>$qubes_name_of_vm</code> ->"
         fi
      fi
      TITLE_CLI="$qubes_name_of_vm | ${VM}${qubes_title_extra} | $(LC_ALL=C date --utc)"
   else
      start_menu_instructions_system_first_part="Start Menu -> System ->"
      persistent_changes_start_menu_instructions_first_part="$start_menu_instructions_system_first_part"
      qubes_title_extra=""
      update_where="this machine"
      passive_popup_title="[$IDENTIFIER]"
      TITLE_CLI="${VM} | $(LC_ALL=C date --utc)"
   fi

   [ -n "$silent" ] || silent="0"

   TITLE_X="$IDENTIFIER | ${VM}${qubes_title_extra}"

   ## No need to add $IDENTIFIER to output, because msgcollector already does that.

   $output_general --icon "$ICON"
   $output_general --parenttty "$my_tty"
   $output_x --titlex "$TITLE_X"
   $output_cli --titlecli "$TITLE_CLI"

   ## Only for systemcheck to make it distinct from other progress bars.
   ## Preventing overlapping windows.
   ## Out-commented. Wayland no longer allows this to work.
   #$output_general --lefttop
}
