#!/bin/bash

## Copyright (C) 2024 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## Copyright (C) 2025 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
## See the file COPYING for copying conditions.

set -o errexit
set -o nounset
set -o errtrace
set -o pipefail

printf "%s\n" "$0: INFO: START" >&2

error_handler() {
   local last_exit_code="$?"
   printf "%s\n" "\
###
$0: ERROR:
BASH_COMMAND: '$BASH_COMMAND'
failed with exit code '$last_exit_code'.
###" >&2
   exit "$last_exit_code"
}

trap error_handler ERR

exit_handler() {
   local exit_code="$?"
   printf "%s\n" "INFO: If installed, rads (Ram Adjusted Desktop Starter) should be starting, ok." >&2
   printf "%s\n" "INFO: (Because sysmaint-boot.target runs 'Before=rads.service' and 'Wants=rads.service'.)" >&2
   if [ "$exit_code" = "0" ]; then
      printf "%s\n" "$0: END: with OK exit code: '$exit_code'" >&2
   else
      printf "%s\n" "$0: END: with ERROR exit code: '$exit_code'" >&2
   fi
   exit "$exit_code"
}

trap exit_handler EXIT

user_sysmaint_split_config_dir='/etc/user-sysmaint-split.conf.d'
kernel_cmdline="$(cat -- /proc/cmdline)"
default_display_manager="$(basename "$(cat -- /etc/X11/default-display-manager)")" || true
sysmaint_session_wayland='no'
sysmaint_autologin='no'
lightdm_home_dir='/var/lib/lightdm'
lightdm_state_dir='/var/lib/lightdm/.cache/lightdm-gtk-greeter'
lightdm_state_file="${lightdm_state_dir}/state"
lightdm_state_backup_file="${lightdm_state_file}.sysmaint-boot"
## no need for an sddm_home_dir variable as sddm_state_dir is also sddm's home
## dir
sddm_state_dir='/var/lib/sddm'
sddm_state_file="${sddm_state_dir}/state.conf"
sddm_state_backup_file="${sddm_state_file}.sysmaint-boot"
xscreensaver_config_file_list=(
   '/etc/X11/app-defaults/XScreenSaver'
   '/usr/lib/X11/app-defaults/XScreenSaver'
)
xscreensaver_backup_config_file_list=(
   '/etc/X11/app-defaults/XScreenSaver.sysmaint-boot'
   '/usr/lib/X11/app-defaults/XScreenSaver.sysmaint-boot'
)
sysmaint_lightdm_conf_file='/etc/lightdm/lightdm.conf.d/zzz-sysmaint-boot.conf'
sysmaint_sddm_conf_file='/etc/sddm.conf.d/zzz-sysmaint-boot.conf'
old_sysmaint_conf_file_list=(
  '/etc/lightdm/lightdm.conf.d/60_sysmaint-boot.conf'
  '/etc/sddm.conf.d/z-sysmaint-boot.conf'
)
sudo_to_sysmaint="sudo --non-interactive -u sysmaint"

sysmaint_account_already_locked=no
if accountctl sysmaint is-pass-locked 2>/dev/null; then
   sysmaint_account_already_locked=yes
fi

lock_sysmaint_account() {
   printf "%s\n" "INFO: sysmaint_account_already_locked: '$sysmaint_account_already_locked'" >&2
   if [ "$sysmaint_account_already_locked" = "no" ]; then
      printf "%s\n" "INFO: Therefore, locking account 'sysmaint' (and deleting auto generated sysmaint login manager configuration files)." >&2
      accountctl sysmaint lock-pass 2>/dev/null
   fi
}

restore_pre_sysmaint_autologin_config() {
   safe-rm -f -- "${sysmaint_lightdm_conf_file}"
   safe-rm -f -- "${sysmaint_sddm_conf_file}"
   safe-rm -f -- "${old_sysmaint_conf_file_list[@]}"
   if [ -f "${lightdm_state_backup_file}" ]; then
      safe-rm -rf -- "${lightdm_state_file}"
      mv --verbose -- "${lightdm_state_backup_file}" "${lightdm_state_file}"
      if [ ! -f "${lightdm_state_file}" ] || ! [ -s "${lightdm_state_file}" ]; then
        safe-rm -rf -- "${lightdm_state_file}"
      fi
   fi
   if [ -f "${sddm_state_backup_file}" ]; then
      safe-rm -rf -- "${sddm_state_file}"
      mv --verbose -- "${sddm_state_backup_file}" "${sddm_state_file}"
      if [ ! -f "${sddm_state_file}" ] || ! [ -s "${sddm_state_file}" ]; then
        safe-rm -rf -- "${sddm_state_file}"
      fi
   fi
}

restore_pre_sysmaint_xscreensaver_config() {
   local i

   for (( i = 0; i < ${#xscreensaver_config_file_list[@]}; i++ )); do
      xscreensaver_config_file="${xscreensaver_config_file_list[i]}"
      xscreensaver_backup_config_file="${xscreensaver_backup_config_file_list[i]}"
      if [ -f "${xscreensaver_backup_config_file}" ]; then
         safe-rm -rf -- "${xscreensaver_config_file}"
         mv --verbose -- "${xscreensaver_backup_config_file}" "${xscreensaver_config_file}"
      fi
   done
}

write_lightdm_enable_autologin_config() {
   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[Seat:*]
autologin-user=sysmaint
autologin-session=sysmaint-session-wayland" \
         | sponge -- "${sysmaint_lightdm_conf_file}"
   else
      printf "%s\n" "[Seat:*]
autologin-user=sysmaint
autologin-session=sysmaint-session" \
         | sponge -- "${sysmaint_lightdm_conf_file}"
   fi
}

write_lightdm_disable_autologin_config() {
   printf "%s\n" "[Seat:*]
autologin-user=" \
      | sponge -- "${sysmaint_lightdm_conf_file}"
}

write_lightdm_sysmaint_default_session_config() {
   if [ -d '/home/sysmaint' ]; then
      if [ "${sysmaint_session_wayland}" = 'yes' ]; then
         printf "%s\n" "[Desktop]
Session=sysmaint-session-wayland" \
            | $sudo_to_sysmaint -- sponge -- '/home/sysmaint/.dmrc'
      else
         printf "%s\n" "[Desktop]
Session=sysmaint-session" \
            | $sudo_to_sysmaint -- sponge -- '/home/sysmaint/.dmrc'
      fi
      printf "%s\n" "INFO: Created file: '/home/sysmaint/.dmrc'" >&2
   else
      printf "%s\n" "WARNING: Folder '/home/sysmaint' does not exist. Therefore, file '/home/sysmaint/.dmrc' has not been created." >&2
   fi

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

   if ! [ -d "${lightdm_state_dir}" ]; then
      safe-rm -rf -- "${lightdm_state_dir}"
      mkdir --verbose --parents -- "${lightdm_state_dir}" || exit 1
      if accountctl lightdm is-user 2>/dev/null \
         && accountctl lightdm is-group 2>/dev/null; then
         ## Not using 'chown -R' here since some of the files under
         ## $lightdm_home_dir may legitimately need to be owned by root:root.
         chown -- lightdm:lightdm "${lightdm_home_dir}"
         chown -- lightdm:lightdm "${lightdm_home_dir}/.cache"
         chown -- lightdm:lightdm "${lightdm_state_dir}"
      fi
   fi

   ## Ensure the state file exists; if it doesn't, we'll make an empty one
   ## here. The undo logic will later detect that this file is empty and
   ## delete it if necessary.
   touch -- "${lightdm_state_file}"
   safe-rm -rf -- "${lightdm_state_backup_file}"
   mv --verbose -- "${lightdm_state_file}" "${lightdm_state_backup_file}"

   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[greeter]
last-user=sysmaint
last-session=sysmaint-session-wayland" \
         | sponge -- "${lightdm_state_file}"
   else
      printf "%s\n" "[greeter]
last-user=sysmaint
last-session=sysmaint-session" \
         | sponge -- "${lightdm_state_file}"
   fi
   printf "%s\n" "INFO: Created file: '${lightdm_state_file}'" >&2
}

write_sddm_enable_autologin_config() {
   # Typically files under /etc/sddm.conf.d are NOT prefixed with a number, for
   # instance KDE generates an /etc/sddm.conf.d/kde_settings.conf file, thus
   # to get this file to be loaded last we have to use a name that sorts last
   # in the alphabet without using a number prefix.
   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[Autologin]
User=sysmaint
Session=sysmaint-session-wayland.desktop" \
         | sponge -- "${sysmaint_sddm_conf_file}"
   else
      printf "%s\n" "[Autologin]
User=sysmaint
Session=sysmaint-session.desktop" \
         | sponge -- "${sysmaint_sddm_conf_file}"
   fi
}

write_sddm_disable_autologin_config() {
   printf "%s\n" "[Autologin]
User=
Session=" \
      | sponge -- "${sysmaint_sddm_conf_file}"
}

write_sddm_sysmaint_default_session_config() {
   if [ -f "${sddm_state_backup_file}" ]; then
      return
   fi

   if ! [ -d "${sddm_state_dir}" ]; then
      safe-rm -rf -- "${sddm_state_dir}"
      mkdir --verbose --parents -- "${sddm_state_dir}" || exit 1
      if accountctl sddm is-user 2>/dev/null \
         && accountctl sddm is-group 2>/dev/null; then
         chown -- sddm:sddm "${sddm_state_dir}"
      fi
   fi

   ## Ensure the state file exists; if it doesn't, we'll make an empty one
   ## here. The undo logic will later detect that this file is empty and
   ## delete it if necessary.
   touch -- "${sddm_state_file}"
   safe-rm -rf -- "${sddm_state_backup_file}"
   mv --verbose -- "${sddm_state_file}" "${sddm_state_backup_file}"

   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[Last]
User=sysmaint
Session=/usr/share/wayland-sessions/sysmaint-session-wayland.desktop" \
         | sponge -- "${sddm_state_file}"
   else
      printf "%s\n" "[Last]
User=sysmaint
Session=/usr/share/xsessions/sysmaint-session.desktop" \
         | sponge -- "${sddm_state_file}"
   fi
   printf "%s\n" "INFO: Created file: '/var/lib/sddm/state.conf'" >&2
}

write_xscreensaver_derived_config() {
   local i

   for (( i = 0; i < ${#xscreensaver_config_file_list[@]}; i++ )); do
      xscreensaver_config_file="${xscreensaver_config_file_list[i]}"
      xscreensaver_backup_config_file="${xscreensaver_backup_config_file_list[i]}"
      if [ -f "${xscreensaver_backup_config_file}" ]; then
         continue
      fi
      if [ -f "${xscreensaver_config_file}" ]; then
         safe-rm -rf -- "${xscreensaver_backup_config_file}"
         cp --verbose -- "${xscreensaver_config_file}" "${xscreensaver_backup_config_file}"

         sed -i '/newLoginCommand/d' "${xscreensaver_config_file}"
         printf "%s\n" "INFO: Removed newLoginCommand from XScreenSaver configuration file '${xscreensaver_config_file}'.
This prevents fast user switching. This is by design. See:
https://www.kicksecure.com/wiki/Sysmaint#Fast_User_Switching" >&2
      fi
   done
}

handle_boot() {
   if ! accountctl sysmaint is-user 2>/dev/null; then
      printf "%s\n" "$0: ERROR: Account 'sysmaint' does not exist. Cleaning up system-wide sysmaint config and exiting." >&2
      restore_pre_sysmaint_autologin_config
      restore_pre_sysmaint_xscreensaver_config
      exit 1
   fi

   if ! [[ "${kernel_cmdline}" =~ 'boot-role=sysmaint' ]]; then
      printf "%s\n" "INFO: USER Session boot detected." >&2
      printf "%s\n" "INFO: (kernel parameter 'boot-role=sysmaint' is not present, ok.)" >&2
      lock_sysmaint_account
      restore_pre_sysmaint_autologin_config
      restore_pre_sysmaint_xscreensaver_config
      exit 0
   fi

   printf "%s\n" "INFO: SYSMAINT Session detected." >&2
   printf "%s\n" "INFO: (kernel parameter 'boot-role=sysmaint' present, ok.)" >&2

   ## Unlocking account 'sysmaint'.
   accountctl sysmaint unlock-pass 2>/dev/null

   if [ "${default_display_manager}" = 'lightdm' ]; then
      mkdir --verbose --parents -- '/etc/lightdm/lightdm.conf.d'

      if [ "${sysmaint_autologin}" = "yes" ]; then
         write_lightdm_enable_autologin_config
      else
         ## Prevent autologin in this instance, or the normal user account will
         ## end up logged in.
         write_lightdm_disable_autologin_config
      fi
      printf "%s\n" "INFO: Created file: '${sysmaint_lightdm_conf_file}'" >&2

      write_lightdm_sysmaint_default_session_config
   elif [ "${default_display_manager}" = 'sddm' ]; then
      mkdir --verbose --parents -- '/etc/sddm.conf.d'

      if [ "${sysmaint_autologin}" = "yes" ]; then
         write_sddm_enable_autologin_config
      else
         ## Prevent autologin in this instance, or the normal user account will
         ## end up logged in
         write_sddm_disable_autologin_config
      fi
      printf "%s\n" "INFO: Created file: '${sysmaint_sddm_conf_file}'" >&2

      write_sddm_sysmaint_default_session_config
   fi

   write_xscreensaver_derived_config

   ## Qubes handling.
   if [ ! -f /usr/share/qubes/marker-vm ]; then
      return
   fi

   printf "%s\n" "INFO: Qubes OS detected. Installing volatile qrexec blacklist." >&2

   while read -r rpc_config_file; do
      rpc_block_path="/run/qubes-rpc/${rpc_config_file}"
      rpc_block_template="#!/bin/bash

printf '%s\n' '${rpc_config_file} qrexec action is prohibited in PERSISTENT Mode - SYSMAINT Session.'"
      printf "%s\n" "${rpc_block_template}" | sponge -- "${rpc_block_path}"
      chmod +x -- "${rpc_block_path}"
   done < /usr/share/user-sysmaint-split/qubes-rpc-blocks
}

parse_user_sysmaint_split_config() {
   local config_file line
   if [ -d "${user_sysmaint_split_config_dir}" ]; then
      for config_file in "${user_sysmaint_split_config_dir}"/*; do
         if [ ! -f "${config_file}" ]; then
            continue
         fi
         while read -r line; do
            case "${line}" in
               'sysmaint-autologin=yes')
                  sysmaint_autologin='yes'
                  ;;
               'sysmaint-autologin=no')
                  sysmaint_autologin='no'
                  ;;
               'sysmaint-session-wayland=yes')
                  sysmaint_session_wayland='yes'
                  ;;
               'sysmaint-session-wayland=no')
                  sysmaint_session_wayland='no'
                  ;;
            esac
         done < "${config_file}"
      done
   fi
   printf "%s\n" "INFO: account 'sysmaint' autologin: '${sysmaint_autologin}'" >&2
   printf "%s\n" "INFO: Wayland session: '${sysmaint_session_wayland}'" >&2
}

remove_sysmaint_qubes() {
   printf "%s\n" "INFO: Qubes unrestricted mode detected. Removing user-sysmaint-split."
   printf "%s\n" "INFO: (kernel parameter 'remove-sysmaint-qubes' is present, ok.)"
   dummy-dependency --yes --purge user-sysmaint-split
}

sysmaint_boot() {
   parse_user_sysmaint_split_config
   local mode="$1"
   case "${mode}" in
      'handle-boot') handle_boot;;
      'cleanup-autologin') restore_pre_sysmaint_autologin_config;;
      'remove-sysmaint-qubes') remove_sysmaint_qubes;;
      'query-sysmaint-autologin')
         trap "" EXIT
         printf "%s\n" "${sysmaint_autologin}"
      ;;
      *)
         printf "%s\n" "$0: ERROR: Invalid mode provided." >&2
         exit 1
      ;;
   esac
}

sysmaint_boot "$@"
