#!/bin/bash -e

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

## provides was_executed
# shellcheck source=../../../../helper-scripts/usr/libexec/helper-scripts/check_runtime.bsh
source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/check_runtime.bsh

## provides has
# shellcheck source=../../../../helper-scripts/usr/libexec/helper-scripts/has.bsh
source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/has.bsh

user_list=()
user_list_already_loaded='false'

run_as_user() {
   local user_name="$1"
   shift
   sudo --non-interactive -u "${user_name}" -- "$@"
}

load_user_list() {
   local user_list_str

   if [ "${user_list_already_loaded}" = 'true' ]; then
      return 0
   fi

   if ! user_list_str="$("${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/get-user-list)"; then
      printf "%s\n" "${0}: ERROR: Failed to get user list!" >&2
      return 1
   fi
   readarray -t user_list <<< "${user_list_str}" || return 1

   user_list_already_loaded='true'
}

command_not_found_sources_list_fix() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1" ]; then
      return 0
   fi

   if [ ! -e "${root}/etc/apt/sources.list" ]; then
      ## https://forums.whonix.org/t/command-not-found-warningcould-not-open-file-etc-apt-sources-list/7903
      touch -- "${root}/etc/apt/sources.list" || true
   fi

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1"
}

command_not_found_permission_fix() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1" ]; then
      return 0
   fi

   if [ -f "${root}/var/lib/command-not-found/commands.db" ]; then
      ## https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=917455
      chmod o+r -- "${root}/var/lib/command-not-found/commands.db" || true
   fi

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1"
}

bisq_desktop_directories_workaround() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ ! -f "${root}/usr/share/whonix/marker" ]; then
      return 0
   fi

   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1" ]; then
      return 0
   fi

   local user_name qubes_vm_name

   user_name="user"
   qubes_vm_name=""
   if ! id -- "${user_name}" >&/dev/null ; then
      return 0
   fi
   if run_as_user "${user_name}" test -f "${root}/home/${user_name}/.local/share/Bisq/btc_mainnet/tor/tor" ; then
      return 0
   fi
   if ! run_as_user "${user_name}" test -d "${root}/home/${user_name}" ; then
      return 0
   fi

   if has qubesdb-read; then
      qubes_vm_name="$(qubesdb-read /name)" || return 0
   fi
   if test -f "${root}/run/qubes/this-is-templatevm" ; then
      return 0
   fi
   ## Avoid running in Qubes DVM Template.
   ## https://phabricator.whonix.org/T726
   if printf '%s\n' "${qubes_vm_name}" | grep -- "-dvm" >/dev/null 2>/dev/null; then
      return 0
   fi

   run_as_user "${user_name}" mkdir -p -- "${root}/home/${user_name}/.local/share/Bisq/btc_mainnet/tor" || true
   run_as_user "${user_name}" touch -- "${root}/home/${user_name}/.local/share/Bisq/btc_mainnet/tor/tor" || true

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1"
}

locales_fix() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1" ]; then
      return 0
   fi

   ## https://github.com/QubesOS/qubes-issues/issues/4889

   local search replace file_name etc_locale_gen_lines

   has str_replace

   search="# en_US.UTF-8 UTF-8"
   replace="en_US.UTF-8 UTF-8"
   file_name="${root}/etc/locale.gen"

   if ! has locale-gen ; then
      ## If the locale-gen program is not available, do not attempt to fix this.
      return 0
   fi

   if ! test -f "${file_name}" ; then
      ## If user deleted that file, do not attempt to fix this.
      ## Could be on purpose.
      return 0
   fi

   etc_locale_gen_lines="$(cat -- "${file_name}" | grep --invert-match -- '\#' | grep --invert-match -- '^$')" || true

   if ! [ "${etc_locale_gen_lines}" = "" ]; then
      ## If file /etc/locale.gen is already non-empty, i.e. has an entry other
      ## blank lines or comments (lines starting with "#") then this should not be needed.
      return 0
   fi

   str_replace "${search}" "${replace}" "${file_name}" || true

   ## Using LANG=C because locale-gen is a perl script and as long as this is
   ## not fixed it would show a warning confusing for users:
   ## "perl: warning: Setting locale failed."
   ## LANG=C only applies to the locale-gen script. Not to the end result of locale-gen.
   ## LANG=C might not be used if already using LC_ALL=C.
   LC_ALL=C LANG=C locale-gen || true

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_1"
}

zsh_migration() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   local do_once_folder do_once_file
   if test -f "${root}/run/qubes/this-is-appvm" ; then
      do_once_folder="${root}/usr/local/var/lib/kicksecure/do_once"
   else
      do_once_folder="${root}/var/lib/kicksecure/do_once"
   fi
   do_once_file="${do_once_folder}/${FUNCNAME[0]}_version_1"

   if [ -f "${do_once_file}" ]; then
      return 0
   fi

   local user_name
   user_name="user"
   if ! id -- "${user_name}" >&/dev/null ; then
      return 0
   fi
   ## https://github.com/Kicksecure/desktop-config-dist/blob/master/etc/skel/.zshrc
   if run_as_user "${user_name}" test -f "${root}/home/${user_name}/.zshrc" ; then
      return 0
   fi
   if ! run_as_user "${user_name}" test -d "${root}/home/${user_name}" ; then
      return 0
   fi

   run_as_user "${user_name}" touch -- "${root}/home/${user_name}/.zshrc" || true

   mkdir --parents -- "${do_once_folder}"
   touch -- "${do_once_file}"
}

## Enables multiline-paste configuration for all active users.
qterminal_confirm_multiline_paste() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2" ]; then
      return 0
   fi

   local user_entry config_file config_file_contents

   load_user_list || return 1
   for user_entry in "${user_list[@]}"; do
      config_file="${root}/home/${user_entry}/.config/qterminal.org/qterminal.ini"
      if ! run_as_user "${user_entry}" test -f "${config_file}" ; then
         continue
      fi
      config_file_contents="$(run_as_user "${user_entry}" cat -- "${config_file}")"
      if run_as_user "${user_entry}" grep -- '^ConfirmMultilinePaste=false$' <<< "${config_file_contents}" >/dev/null; then
         # shellcheck disable=SC2001
         config_file_contents="$(run_as_user "${user_entry}" sed -- 's/^ConfirmMultilinePaste=false$/ConfirmMultilinePaste=true/' <<< "${config_file_contents}")"
         printf '%s\n' "${config_file_contents}" | run_as_user "${user_entry}" sponge -- "${config_file}"
      fi
   done

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2"
}

## Fixes an issue where QTerminal attempts to save its bookmarks file in the
## home folder of a user account other than the account QTerminal is
## running as.
qterminal_bookmarks_file() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2" ]; then
      return 0
   fi

   local user_entry config_file

   load_user_list || return 1
   for user_entry in "${user_list[@]}"; do
      config_file="${root}/home/${user_entry}/.config/qterminal.org/qterminal.ini"
      if ! run_as_user "${user_entry}" test -f "${config_file}" ; then
         continue
      fi
      run_as_user "${user_entry}" sed -i -- '/BookmarksFile=\/home\/user\/.config\/qterminal.org\/qterminal_bookmarks.xml/d' "${config_file}"
   done

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2"
}

## Fixes removable media automount being enabled by default in pcmanfm-qt
## (this is a security risk)
pcmanfm_qt_removable_media_automount() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2" ]; then
      return 0
   fi
   local user_entry config_file config_file_variant_list config_file_variant

   load_user_list || return 1
   for user_entry in "${user_list[@]}"; do
      config_file_variant_list=(
         'lxqt'
         'default'
      )
      for config_file_variant in "${config_file_variant_list[@]}"; do
         config_file="${root}/home/${user_entry}/.config/pcmanfm-qt/${config_file_variant}/settings.conf"
         if ! run_as_user "${user_entry}" test -f "${config_file}" ; then
            continue
         fi
         run_as_user "${user_entry}" sed -i -- 's/^MountOnStartup=true$/MountOnStartup=false/' "${config_file}"
         run_as_user "${user_entry}" sed -i -- 's/^MountRemovable=true$/MountRemovable=false/' "${config_file}"
      done
   done

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2"
}

## Fixes the default archiver being xarchiver (which isn't installed by
## default)
pcmanfm_qt_archiver() {
   local root="${LEGACY_DIST_TEST_ROOT:-}"
   if [ -f "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2" ]; then
      return 0
   fi

   local user_entry config_file config_file_variant_list config_file_variant

   load_user_list || return 1
   for user_entry in "${user_list[@]}"; do
      config_file_variant_list=(
         'lxqt'
         'default'
      )
      for config_file_variant in "${config_file_variant_list[@]}"; do
         config_file="${root}/home/${user_entry}/.config/pcmanfm-qt/${config_file_variant}/settings.conf"
         if ! run_as_user "${user_entry}" test -f "${config_file}" ; then
            continue
         fi
         run_as_user "${user_entry}" sed -i -- 's/^Archiver=xarchiver$/Archiver=lxqt-archiver/' "${config_file}"
      done
   done

   mkdir --parents -- "${root}/var/lib/whonix/do_once"
   touch -- "${root}/var/lib/whonix/do_once/${FUNCNAME[0]}_version_2"
}

## Removes insecure MOK keys that were accidentally shipped in installation
## images previously, but only if it is safe to do so.
##
## Note that this function CANNOT be placed in either legacy-dist.preinst or
## legacy-dist.postinst. If the user has a vulnerable key enrolled in their
## firmware, starts the process of resetting the MOK keys, but then generates
## new keys before rebooting and finishing the reset process, helper-scripts'
## 'check-image-builtin-mok' script will detect what it thinks is a
## vulnerable-but-not-enrolled key. What *should* be done in this situation is
## the key should be deleted, but if the deletion logic is in a package
## maintainer script, it won't be run again to remove the new keys. This will
## result in a confusing systemcheck warning about needing to upgrade
## legacy-dist.
##
## Theoretically, we could allow users to generate their new MOK keys after
## starting a MOK reset but before finishing it. Doing so would require saving
## a hash of the old insecure key somewhere so that we could tell that the new
## key was a different key. This probably isn't a good idea though, because if
## the user generates a new key using the shim-enroll-mok helper, the new key
## creation request will override the pending MOK reset request. Then when the
## user reboots and enrolls the newly generated key, the vulnerable key will
## still be present in the MOK store. If we relied on the hash trick above,
## 'check-image-builtin-mok' would then see that the existing key is
## different, and assume the system is safe, when in reality it is still
## vulnerable.
##
## We could work around *that* too by dumping the MOK store and checking to
## see if any of the keys are the previously detected vulnerable key, but
## it's far simpler if we just require that new MOK keys are only generated
## after the reset is complete. Putting this function here implements that.
secure_boot_mok_cleanup() {
   local check_image_builtin_mok_retcode

   check_image_builtin_mok_retcode='0'
   ## Note that 'check-image-builtin-mok' handles the do_once mechanism
   ## itself.
   "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/check-image-builtin-mok || check_image_builtin_mok_retcode="$?"
   if [ "${check_image_builtin_mok_retcode}" = '1' ]; then
      ## Vulnerable key is present but may be safely wiped, delete them

      # shellcheck source=../../../../helper-scripts/usr/sbin/shim-signed-mok-setup
      source "${HELPER_SCRIPTS_PATH:-}"/usr/sbin/shim-signed-mok-setup
      dkms_mok_variables_set

      # shellcheck disable=SC2154
      safe-rm --force -- "${dkms_mok_public_file}"
      # shellcheck disable=SC2154
      safe-rm --force -- "${dkms_mok_private_file}"
   fi
}

main() {
   set -o errexit
   set -o nounset
   set -o pipefail
   set -o errtrace
   shopt -s inherit_errexit
   shopt -s shift_verbose
   export LC_ALL=C

   if "${HELPER_SCRIPTS_PATH:-}/usr/libexec/helper-scripts/debug-kicksecure-enabled" ; then
     set -x
   fi

   true "${0}: START"

   if ! [ "$(id -u)" = "0" ]; then
      printf '%s\n' "${0} ERROR: must run as root!" >&2
      exit 1
   fi

   ## Fix for:
   ## dpkg-maintscript-helper: error: couldn't identify the package
   [ -n "${DPKG_MAINTSCRIPT_PACKAGE:-}" ] || DPKG_MAINTSCRIPT_PACKAGE="legacy-dist"
   [ -n "${DPKG_MAINTSCRIPT_NAME:-}" ] || DPKG_MAINTSCRIPT_NAME="preinst"
   export DPKG_MAINTSCRIPT_PACKAGE DPKG_MAINTSCRIPT_NAME
   timeout --kill-after 60 60 /var/lib/dpkg/info/legacy-dist.preinst install || true

   ## automate Tor permission fix
   ## https://www.whonix.org/wiki/Tor#Permissions_Fix
   ## https://forums.whonix.org/t/tor-error-your-tor-config-file-contains-at-least-one-error-var-lib-tor-tor-cannot-be-read-permission-denied/6200
   ## cat -- /var/lib/dpkg/info/tor.postinst | grep -- chown
   ## chown debian-tor:debian-tor /var/lib/tor
   ## chown debian-tor:adm /var/log/tor
   if [ -d "/var/lib/tor" ]; then
      chown --recursive debian-tor:debian-tor -- /var/lib/tor || true
   fi
   if [ -d "/var/log/tor" ]; then
      chown --recursive debian-tor:adm -- /var/log/tor || true
   fi

   command_not_found_sources_list_fix || true

   command_not_found_permission_fix || true

   bisq_desktop_directories_workaround || true

   locales_fix || true

   zsh_migration || true

   qterminal_confirm_multiline_paste || true

   qterminal_bookmarks_file || true

   pcmanfm_qt_removable_media_automount || true

   pcmanfm_qt_archiver || true

   secure_boot_mok_cleanup || true

   true "${0}: END"
}

if was_executed "${BASH_SOURCE[0]}"; then
   main "$@"
fi
