#!/bin/bash

## Copyright (C) 2020 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@kicksecure.com>
## See the file COPYING for copying conditions.

## Nearly every function here is reached only through the option dispatch or
## a trap, which shellcheck reads as unreachable.
# shellcheck disable=SC2317

set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose

## Set by the image build (dist_build_virtualbox) and by the caller
## (output_command), neither of which exists when this runs on an installed
## system -- and both are read UNCONDITIONALLY below, including on the
## fail-closed path whose whole job is to report the build variable's value.
## Empty is what they expanded to before strict mode.
dist_build_virtualbox="${dist_build_virtualbox:-}"
## Defaulting output_command to EMPTY was wrong: '${output_command} "msg"'
## with an empty value makes bash run "msg" ITSELF as the command, so every
## message became 'command not found' (127) -- and under errexit that fired the
## ERR trap, which uses ${output_command} too and failed the same way. The
## abort just moved from "unbound variable" to "command not found".
##
## A printf wrapper rather than 'echo': it appends the newline the call sites
## expect and does not interpret a '%' in the message.
output_default() {
  printf '%s\n' "$@"
}
output_command="${output_command:-output_default}"

fail_closed_maybe() {
   if [ "${dist_build_virtualbox}" = 'true' ]; then
     ${output_command} "$0 ERROR: Variable 'dist_build_virtualbox' is set to 'true', therefore failing closed." >&2
     exit 1
   fi
}

output_more_information() {
   ${output_command} ""
   ${output_command} "$0 INFO: This is not about security."
   ${output_command} "$0 INFO: This is about usability only."
   ${output_command} "$0 INFO: If you do not need or have no issues with full screen, clipboard sharing, mouse capture or shared folder this can be safely ignored."
   ${output_command} "$0 INFO: For more information, see:"
   ${output_command} "https://www.kicksecure.com/wiki/ga"
}

virtualbox_guest_additions_packages_still_installed() {
   ${output_command} "$0 INFO: VirtualBox guest additions Debian package '$1' already installed."
   ${output_command} "$0 INFO: Will not install VirtualBox guest additions from package 'virtualbox-guest-additions-iso'."
   ${output_command} "$0 INFO: This is OK."
   ${output_command} "$0 INFO: For more information, see:"
   ${output_command} "https://www.kicksecure.com/wiki/VirtualBox/Guest_Additions#vbox-guest-installer"
   fail_closed_maybe
   exit 0
}

## {{{ taken from https://github.com/dell/dkms/blob/master/dkms_common.postinst

## GPLv2

# Copyright (C) 2002-2005 Flavio Stanchina
# Copyright (C) 2005-2006 Aric Cyr
# Copyright (C) 2007 Mario Limonciello
# Copyright (C) 2009 Alberto Milone

## The kernel-selection helpers below are vendored from VirtualBox's own
## guest-additions tooling -- different indentation, uppercase names and
## single-'#' comments give it away. shellcheck misreads its optional
## arguments and its dispatch; flagged rather than restructured, because
## rewriting vendored code is not this pass's business.
# shellcheck disable=SC2119,SC2120

# Get the most recent kernel on Debian based systems. This keeps
# into account both the version and the ABI. If the current kernel
# is the most recent kernel then the function will print a null string.
_get_newest_kernel_debian() {
    NEWEST_KERNEL=
    NEWEST_VERSION=
    NEWEST_ABI=

    for kernel in /boot/config-*; do
        [ -f "${kernel}" ] || continue
        KERNEL=${kernel#*-}
        KERNEL_VERSION=${KERNEL%%-*}
        ABI=${KERNEL#*-}
        ABI=${ABI%%-*}

        if [ -z "${NEWEST_KERNEL}" ]; then
            # The 1st time get a version which is bigger than $1
            ## ':-' because the ONE call site -- the chroot branch below --
            ## passes no argument at all, so this reads an unset $1 on the first
            ## /boot/config-* found, which is every run. Empty is what it
            ## expanded to before nounset, and it means "bigger than nothing",
            ## so the first kernel seen becomes the baseline.
            COMPARE_TO="${1:-}"
        else
            # Get the biggest version
            COMPARE_TO="${NEWEST_VERSION}-${NEWEST_ABI}"
        fi

        # if $kernel is greater than $COMPARE_TO
        if [ "$(dpkg --compare-versions "${KERNEL_VERSION}-${ABI}" ge "${COMPARE_TO}" && printf '%s\n' "yes" || \
              printf '%s\n' "no")" = "yes" ]; then
            NEWEST_KERNEL=${KERNEL}
            NEWEST_VERSION=${KERNEL_VERSION}
            NEWEST_ABI=${ABI}
        fi
    done

    printf '%s\n' "${NEWEST_KERNEL}"
}

## }}}

error_handler() {
   ${output_command} "$0 ERROR: BASH_COMMAND: '${BASH_COMMAND}' failed with exit code '$?'." >&2
   ${output_command} "$0 ERROR: Please search Kicksecure forums and report if required." >&2
   output_more_information
   fail_closed_maybe
   exit 0
}

if test -o xtrace ; then
   output_command='true'
else
   output_command='printf %s\n'
fi

trap error_handler ERR

printf '%s\n' "$0 (part of package 'vm-config-dist') INFO: This is VirtualBox guest additions from Debian package 'virtualbox-guest-additions-iso' installation helper by Kicksecure developers."

if [ "$(id -u)" != "0" ]; then
   ${output_command} "$0 ERROR: Must run as root. Run:"
   ${output_command} "sudo $0"
   exit 112
fi

if [ "${dist_build_virtualbox}" = "true" ] || [ -f '/var/lib/dist-base-files/live_build' ] || [ "$(systemd-detect-virt)" = "oracle" ]; then
   true "INFO: dist_build_virtualbox=true, or '/var/lib/dist-base-files/live_build' exists, or systemd-detect-virt returned oracle, therefore proceed."
else
   printf '%s\n' "$0 (part of package 'vm-config-dist') INFO: Neither variable 'dist_build_virtualbox=true' is set, nor does '/var/lib/dist-base-files/live_build' exist, nor 'systemd-detect-virt' returned 'oracle', therefore doing nothing, ok."
   exit 0
fi

if ischroot --default-false ; then
   printf '%s\n' "$0 INFO: chroot detected."
   ## /opt/VBoxGuestAdditions-6.1.18/init/vboxadd uses:
   # test -z "${TARGET_VER}" && TARGET_VER=`uname -r`
   #
   #if test -d /lib/modules/"$TARGET_VER"/build; then
   #   setup_modules "$TARGET_VER"
   #   depmod
   #else
   #   info "Kernel headers not found for target kernel $TARGET_VER. \
#Please install them and execute
#/sbin/rcvboxadd setup"
   #fi
   ## Output of `uname -r` is host kernel version.
   ## Not build chroot kernel version.
   ## Therefore building for that version would fail.
   ## Hence, manually setting to correct version.
   TARGET_VER="$(_get_newest_kernel_debian)"
   export TARGET_VER
   printf '%s\n' "$0 INFO: chroot detected. Therefore: export TARGET_VER='${TARGET_VER}'"
   if [ "${TARGET_VER}" = "" ]; then
      printf '%s\n' "$0 ERROR: variable TARGET_VER is empty. Failing closed. Exit error."
      exit 1
   fi
fi

if package-installed-check "virtualbox-guest-x11" ; then
   virtualbox_guest_additions_packages_still_installed virtualbox-guest-x11
fi
if package-installed-check "virtualbox-guest-utils" ; then
   virtualbox_guest_additions_packages_still_installed virtualbox-guest-utils
fi

if package-installed-check "virtualbox-guest-additions-iso" ; then
   ${output_command} "$0 INFO: VirtualBox guest additions package 'virtualbox-guest-additions-iso' installed."
else
   ${output_command} "$0 ERROR: package 'virtualbox-guest-additions-iso' not installed! To install, run:" >&2
   ${output_command} "sudo apt update" >&2
   ${output_command} "sudo apt install virtualbox-guest-additions-iso" >&2
   output_more_information
   exit 0
fi

safe-rm -r -f -- /var/cache/vm-config-dist/vbox-guest-additions-extracted-iso
safe-rm -r -f -- /var/cache/vm-config-dist/vbox-guest-additions-extracted-makeself

mkdir -p -- /var/cache/vm-config-dist/vbox-guest-additions-extracted-iso

pushd -- /var/cache/vm-config-dist/vbox-guest-additions-extracted-iso >/dev/null

printf '%s\n' "$0 INFO: Extracting file '/usr/share/virtualbox/VBoxGuestAdditions.iso' (from package 'virtualbox-guest-additions-iso') to folder '/var/cache/vm-config-dist/vbox-guest-additions-extracted-iso' now..."
7z x -o/var/cache/vm-config-dist/vbox-guest-additions-extracted-iso /usr/share/virtualbox/VBoxGuestAdditions.iso >/dev/null

chmod +x -- VBoxLinuxAdditions.run

printf '%s\n' "$0 INFO: Running '/var/cache/vm-config-dist/vbox-guest-additions-extracted-iso/VBoxLinuxAdditions.run --check' now..."
./VBoxLinuxAdditions.run --check >/dev/null

## Could run VBoxLinuxAdditions.run directly but extracting it allows easier debugging in case of failure.
#./VBoxLinuxAdditions.run

## Add new line because './VBoxLinuxAdditions.run --check' did not.
printf '%s\n' ""
printf '%s\n' "$0 INFO: Extracting file '/var/cache/vm-config-dist/vbox-guest-additions-extracted-iso/VBoxLinuxAdditions.run' to folder '/var/cache/vm-config-dist/vbox-guest-additions-extracted-makeself' now..."
./VBoxLinuxAdditions.run --noexec --keep --target /var/cache/vm-config-dist/vbox-guest-additions-extracted-makeself

popd >/dev/null

pushd -- /var/cache/vm-config-dist/vbox-guest-additions-extracted-makeself >/dev/null

installer_exit_code=0

printf '%s\n' "$0 INFO: Running '/var/cache/vm-config-dist/vbox-guest-additions-extracted-makeself/install.sh force force' now..."
./install.sh force force || { installer_exit_code=$? ; true; };

printf '%s\n' "$0 INFO: /var/cache/vm-config-dist/vbox-guest-additions-extracted-makeself/install.sh exited with exit code '${installer_exit_code}'."

## /etc/xdg/autostart/vboxclient.desktop is shipped by the VirtualBox Guest
## Additions installer and competes with our own
## /usr/lib/systemd/user/vboxclient-wayland.service - Therefore deleting it.
## Do NOT delete /etc/xdg/autostart/vboxclient-wayland.desktop - that is our own
## autostart file (shipped by this package) which is what starts the systemd
## unit; removing it would disable Wayland clipboard sharing entirely.
## https://forums.whonix.org/t/how-to-enable-shared-clipboard-drag-and-drop-features-inside-virtualbox-with-whonix-lxqt-18-0-8-7/22588/100
safe-rm -f -- /etc/xdg/autostart/vboxclient.desktop

if [ "${installer_exit_code}" = 0 ] || [ "${installer_exit_code}" = 2 ]; then
   ${output_command} "$0 INFO: Success, installed VirtualBox guest additions from package 'virtualbox-guest-additions-iso'."
   output_more_information
else
   ${output_command} "$0 WARNING: Might have failed to install VirtualBox guest additions from package 'virtualbox-guest-additions-iso'."
   ${output_command} "$0 INFO: You could have a look at the installation log file. Run:"
   ${output_command} "cat /var/log/vboxadd-install.log"
   output_more_information
   fail_closed_maybe
fi

popd >/dev/null

exit 0
