#!/bin/bash

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

## Static-analysis waivers for this legacy, sourced build library:
## - intentional word-splitting of command arguments;
## - 'command -v' for dependency probing during early bootstrap, before the
##   'has' helper is guaranteed present;
## - 'rm' of specific, known files (not user-supplied paths).
# shellcheck disable=SC2086
## style-ok: no-has
## style-ok: no-safe-rm

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

true "${BASH_SOURCE[0]} INFO: begin"

## Silence xtrace while sourcing parse-cmd and running the parser, to
## avoid flooding output with irrelevant parser trace lines. Restore
## the previous xtrace state afterwards.
xtrace_off

#printf "%s\n" "INFO: Currently running script: ${BASH_SOURCE[0]}"

## {{ Sanity Tests.

dist_build_missing_dependency() {
   error "dist_build_missing_dependency
programs_to_check_installed_item: $1

Did you install dependencies as mentioned in build documentation chapter
Host Preparation
?"
   true
}

variables_script_sanity_test() {
   local dist_build_programs_to_check_installed_list programs_to_check_installed_item
   dist_build_programs_to_check_installed_list="logname whoami dirname basename awk grep"

   ## None of the programs below need root, so do not invoke them via sudo.
   ## Using '$SUDO_TO_ROOT' here would also be wrong because SUDO_TO_ROOT
   ## is only initialized further down in this file.
   for programs_to_check_installed_item in $dist_build_programs_to_check_installed_list ; do
      command -v "$programs_to_check_installed_item" >/dev/null || dist_build_missing_dependency "$programs_to_check_installed_item"
   done
}

variables_script_sanity_test

## }}

path_variable_adjustments() {
   local path_part path_array local_sbin_found standard_sbin_found old_path new_path

   ## Manual testing.
   ## CI has the following and lacks any mention of 'sbin' in the PATH variable.
   #PATH="/usr/local/bin:/usr/bin:/bin:/usr/games"

   old_path="$PATH"
   true "INFO: old_path: $old_path"
   ## Example PATH:
   ## /home/user/derivative-maker/help-steps:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
   ## Example PATH on CI:
   #PATH="/usr/local/bin:/usr/bin:/bin:/usr/games"

   IFS=':' read -ra path_array <<< "$PATH"

   new_path="$PATH"
   for path_part in "${path_array[@]}"; do
      if [ "${path_part:-}" = "/usr/local/sbin" ]; then
         local_sbin_found=yes
      fi
      if [ "${path_part:-}" = "/usr/sbin" ]; then
         standard_sbin_found=yes
      fi
   done

   if [ ! "${local_sbin_found:-}" = "yes" ]; then
      new_path="/usr/local/sbin:$new_path"
   fi

   if [ ! "${standard_sbin_found:-}" = "yes" ]; then
      new_path="$new_path:/usr/sbin"
   fi

   export PATH="$new_path"
   true "INFO: new_path: $PATH"
}

path_variable_adjustments
## Example PATH:
## /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

## Determine the invoking (non-root) user. Order of precedence matters:
## 1. Honor a caller-provided 'user_name'.
## 2. '$SUDO_USER' - correct when the script is run via 'sudo'.
## 3. 'logname' - reads the controlling tty's owner; correct on an
##    interactive terminal but fails without a tty (e.g. some CI jobs).
## 4. 'whoami' / '$USER' - only trusted if we're not currently root,
##    because otherwise they would yield 'root' and 'HOMEVAR=/home/root'
##    would silently point at a nonexistent home directory.
[ -n "${user_name:-}" ] || user_name="${SUDO_USER:-}"
[ -n "${user_name:-}" ] || user_name="$(logname 2>/dev/null)" || true
if [ -z "${user_name:-}" ] && [ "$EUID" != "0" ]; then
   user_name="$(whoami)" || true
   [ -n "${user_name:-}" ] || user_name="${USER:-}"
fi
if [ "${user_name:-}" = "" ]; then
   error "Variable user_name is empty."
fi
export user_name

[ -n "${SUDO_TO_ROOT:-}" ] || SUDO_TO_ROOT="sudo --non-interactive"
[ -n "${HOMEVAR_VBOX_TEMP:-}" ] || HOMEVAR_VBOX_TEMP="/home/dm-vbox-temp"
export HOMEVAR_VBOX_TEMP

## Use 'sudo' with '-D $HOMEVAR_VBOX_TEMP' ('--chdir') to avoid 'VBoxManage' error.
#> VBoxManage: error: Could not create the directory '.' (VERR_ACCESS_DENIED)
[ -n "${SUDO_TO_VBOX_TEMP:-}" ] || SUDO_TO_VBOX_TEMP="sudo --non-interactive -D $HOMEVAR_VBOX_TEMP -u dm-vbox-temp"

[ -n "${HOMEVAR:-}" ] || HOMEVAR="/home/$user_name"
export HOMEVAR
[ -n "${binary_build_folder_dist:-}" ] || binary_build_folder_dist="$HOMEVAR/derivative-binary"
export binary_build_folder_dist

MYDIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" && pwd )"
[ -n "${source_code_folder_dist:-}" ] || source_code_folder_dist="$(dirname -- "$MYDIR")"
export source_code_folder_dist

## '$binary_build_folder_dist' and '$source_code_folder_dist' are bind-mounted
## into the cowbuilder chroot for the VirtualBox VM steps -- see
## 'build-steps.d/4600_create-vbox-vm' and 'dm-prepare-release' (in
## developer-meta-files), which pass them via 'cowbuilder --bindmounts'.
## pbuilder word-splits BINDMOUNTS on whitespace internally
## ('for i in $BINDMOUNTS'), so a path containing whitespace silently breaks the
## bind-mount. Fail early here, with a clear message, instead of late with a
## cryptic pbuilder error.
for space_check_var in binary_build_folder_dist source_code_folder_dist ; do
   case "${!space_check_var}" in
      *[[:space:]]*)
         error "${space_check_var} must not contain whitespace (it is bind-mounted into the cowbuilder chroot and pbuilder word-splits BINDMOUNTS): '${!space_check_var}'"
         ;;
   esac
done
unset space_check_var

[ -n "${dist_parent_folder:-}" ] || dist_parent_folder="$(dirname -- "$source_code_folder_dist")"
[ -n "${dist_developer_meta_files_folder:-}" ] || dist_developer_meta_files_folder="${source_code_folder_dist}/packages/kicksecure/developer-meta-files"

[ -n "${dist_source_parentdir:-}" ] || dist_source_parentdir="$(dirname -- "$source_code_folder_dist")"
export dist_source_parentdir

[ -n "${dist_source_help_steps_folder:-}" ] || dist_source_help_steps_folder="${source_code_folder_dist}/help-steps"
export dist_source_help_steps_folder

[ -n "${HELPER_SCRIPTS_PATH:-}" ] || HELPER_SCRIPTS_PATH="${source_code_folder_dist}/packages/kicksecure/helper-scripts"
export HELPER_SCRIPTS_PATH

[ -n "${str_replace_tool:-}" ] || str_replace_tool="${HELPER_SCRIPTS_PATH}/usr/bin/str_replace"
[ -n "${str_replace_many_tool:-}" ] || str_replace_many_tool="${HELPER_SCRIPTS_PATH}/usr/bin/str-replace-many"

## All Debian architectures plus the literal 'source'. 'source' is a
## valid Debian "architecture" value referring to source packages (see
## 'dpkg-architecture', 'sources.list' Architectures= field). It is not
## a typo and it is not shadowing the bash 'source' builtin because
## 'architecture_all_list' is an array.
[ -n "${architecture_all_list:-}" ] || architecture_all_list=( amd64 arm64 armel armhf hurd-i386 hurd-amd64 i386 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc ppc64 ppc64el s390x sparc source )

## NOTE: This 'cd' is by design. Sourcing 'variables' intentionally
## changes the caller's working directory to 'source_code_folder_dist'.
## Several subsequent commands in this file (e.g. 'dpkg-parsechangelog'
## reading debian/changelog, 'git describe' reading the repo HEAD) and
## downstream build-step scripts rely on being in the derivative-maker
## source tree. Callers that source 'variables' must be aware of this
## side effect and either accept it or save/restore their own CWD.
cd -- "$source_code_folder_dist"

[ -n "${dist_aptgetopt_file:-}" ] || dist_aptgetopt_file="$binary_build_folder_dist/30_derivative-maker.conf"
export dist_aptgetopt_file
$SUDO_TO_ROOT rm --force -- "$dist_aptgetopt_file"

## DIST_ prefix avoids a name collision with pbuilder/cowbuilder, which use
## their own array variable 'APTGETOPT' (see pbuilderrc) that gets sourced
## inside the cowbuilder chroot scripts (help-steps/pbuilder-chroot-script-*).
## Cannot export bash array.
#DIST_APTGETOPT=()
#export DIST_APTGETOPT
export DIST_APTGETOPT_SERIALIZED

## Debugging.
#declare -p DIST_APTGETOPT
#printf "%s\n" "$DIST_APTGETOPT_SERIALIZED"

aptgetopt_add() {
   local opt
   opt="$1"

   DIST_APTGETOPT+=("-o")
   DIST_APTGETOPT+=("$opt")
   DIST_APTGETOPT_SERIALIZED="$(printf '%s\n' "${DIST_APTGETOPT[@]}")"

   if [[ "${opt:-}" =~ [Pp]roxy ]]; then
      return 0
   fi
   DIST_APTGETOPT_WITHOUT_APT_CACHE+=("-o")
   DIST_APTGETOPT_WITHOUT_APT_CACHE+=("$opt")
}

aptgetopt_conf_add () {
   mkdir --parents -- "$binary_build_folder_dist/"
   printf "%s\n" "$1" | tee --append -- "$dist_aptgetopt_file" >/dev/null
}

## Avoiding "perl: warning: Setting locale failed." as suggested on
## https://wiki.ubuntu.com/DebootstrapChroot and
## https://lists.debian.org/debian-amd64/2005/08/msg00249.html.
LANG="C"
export LANG
LC_ALL="C"
export LC_ALL

## Deterministic.
TZ=UTC
export TZ

## Disable uwt while building Whonix,
## because it is not functional while building Whonix from source code.
[ -n "${UWT_DEV_PASSTHROUGH:-}" ] || UWT_DEV_PASSTHROUGH="1"
export UWT_DEV_PASSTHROUGH

## used by mmdebstrap
## dpkg-parsechangelog (from package dpkg-dev) is not available before dependencies are installed.
if command -v dpkg-parsechangelog &>/dev/null ; then
   SOURCE_DATE_EPOCH="$(dpkg-parsechangelog --show-field=Timestamp)"
else
   true "INFO: SOURCE_DATE_EPOCH not available yet because dpkg-parsechangelog has not been installed yet."
fi

## TODO: do not export without thinking this through since also used by debhelper
export SOURCE_DATE_EPOCH

#[ -n "${DEBDEBUG:-}" ] || export DEBDEBUG="1"

[ -n "${tpo_downloader_debug:-}" ] || tpo_downloader_debug="1"
export tpo_downloader_debug

if [ ! "${dist_build_one_parsed:-}" = "true" ]; then
   bash -n -- "${source_code_folder_dist}/help-steps/parse-cmd"
   source "${source_code_folder_dist}/help-steps/parse-cmd"
   dist_build_one_parse_cmd "$@"
   dist_build_one_parsed="true"
   export dist_build_one_parsed
fi

[ -n "${dist_build_target_arch:-}" ] || dist_build_target_arch="$(dpkg --print-architecture)"
export dist_build_target_arch

## Architecture-derived defaults. Kept here (not in parse-cmd) so internal
## helper scripts that pre-set 'dist_build_one_parsed=true' to skip parse-cmd
## (e.g. dm-tor-update-repository, dm-reprepro-wrapper) still get sensible
## values for these variables.
if [ "${dist_build_target_arch:-}" = "amd64" ]; then
   ## https://forums.whonix.org/t/long-wiki-edits-thread/3477/2009
   [ -n "${target_architecture_pretty_name:-}" ] || target_architecture_pretty_name="Intel_AMD64"
fi
[ -n "${target_architecture_pretty_name:-}" ] || target_architecture_pretty_name="${dist_build_target_arch:-}"
[ -n "${BUILD_KERNEL_PKGS:-}" ] || BUILD_KERNEL_PKGS="linux-image-${dist_build_target_arch:-}"
[ -n "${BUILD_HEADER_PKGS:-}" ] || BUILD_HEADER_PKGS="linux-headers-${dist_build_target_arch:-}"
export target_architecture_pretty_name
export BUILD_KERNEL_PKGS
export BUILD_HEADER_PKGS

## Default the user-overridable verification flags to "false" so that
## 'help-steps/git_sanity_test' can require them to be set explicitly
## (security: caller must make an explicit decision; no implicit defaults
## inside the verification script). 'parse-cmd' sets them to "true" when
## --allow-untagged / --allow-uncommitted are passed.
[ -n "${dist_build_ignore_untagged:-}" ]    || dist_build_ignore_untagged="false"
[ -n "${dist_build_ignore_uncommitted:-}" ] || dist_build_ignore_uncommitted="false"
export dist_build_ignore_untagged
export dist_build_ignore_uncommitted

## Default to "false" so callers can use literal-string equality tests
## (!= 'true' / = 'true') without guarding the unset case. 'parse-cmd' sets
## this to "true"/"false" when --remote-derivative-packages is passed. "false"
## is the normal path: build derivative packages locally and serve them from
## the local temp apt repo instead of pulling them from a remote repository.
[ -n "${build_remote_derivative_pkgs:-}" ] || build_remote_derivative_pkgs="false"
export build_remote_derivative_pkgs

## Default to "false" so callers can use literal-string equality
## tests (= 'false' / = 'true') without having to second-guess the
## unset case. dm-build-official-one sets dist_build_redistributable
## to "true" for redistributable builds; CI is set to "true" by
## GitHub Actions and most other CI runners.
[ -n "${dist_build_redistributable:-}" ] || dist_build_redistributable="false"
[ -n "${CI:-}" ]                         || CI="false"

## Configuration precedence (lowest -> highest):
##   1. Code defaults (this file)
##   2. ${source_code_folder_dist}/buildconfig.d/*  (in-tree, ships with code)
##   3. /etc/buildconfig-dist.d/*                   (system override)
##   4. ${HOMEVAR}/buildconfig.d/*                  (user override)
##   5. Environment variables
##   6. Command-line options (parse-cmd)
##
## All defaults below MUST use '[ -n "${var:-}" ] ||' or '[[ -v var ]] ||'
## so that anything already set by parse-cmd / env / buildconfig.d wins.
## Validation that depends on the final value (e.g. the redistributable
## --repo check) is performed AFTER buildconfig.d sourcing further down.
##
## NOTE: Variable 'dist_build_redistributable=true' is set in 'help-steps/dm-build-official'.
if [ "${dist_build_redistributable:-}" = "true" ]; then
   if [ "${CI:-}" = "true" ]; then
      ## Compatible with git shallow clone.
      ## By design. CI is untrusted. Compromised CI is out-of-scope and not
      ## part of the threat model. Using 'HEAD' as the trust root is
      ## acceptable because CI environments are not expected to provide
      ## stronger guarantees than TOFU.
      [[ -v sq_git_trust_root ]] || sq_git_trust_root="HEAD"
   else
      ## Use "safer" 'sq-git log' root of trust ('--trust-root').
      ## Requires full (not shallow) git clone.
      ## Incompatible with shallow git clone.
      ## Using the git commit ID where derivative-maker 'openpgp-policy.toml' file
      ## was first introduced.
      ## See also: 'buildconfig.d/30_signing_key.conf'
      ## Older. Broken due to lack of using "git merge" without "--no-ff".
      #[[ -v sq_git_trust_root ]] || sq_git_trust_root="93d79d25ad28539a0fea503fcf9ea9f12b683d72"
      ## Broken due to SSH signed git commits which are unsupported by 'sq-git'.
      #[[ -v sq_git_trust_root ]] || sq_git_trust_root="ac5e8736d2e2d5b2d44a6e2e84eedfe578e837d7"
      ## Also use 'HEAD' since source of issues with unknown security gain.
      [[ -v sq_git_trust_root ]] || sq_git_trust_root="HEAD"
   fi

   ## Enable binary derivative repository by default for official builds.
   ## Same as: '--repo true'
   ## Defaulted (not unconditionally assigned) so that '--repo false' /
   ## env / buildconfig.d can still take effect; an explicit 'false' is
   ## then validated below after buildconfig.d sourcing.
   [ -n "${build_remote_repo_enable:-}" ] || build_remote_repo_enable="true"
   export build_remote_repo_enable

   if [ "${dist_build_target_arch:-}" = "amd64" ]; then
      ## Make sure official builds for the Intel/AMD64 architecture
      ## come with Tor Browser installed by default.
      ## Fail closed by default in case of Tor Browser download issues.
      ## Same as: '--tb closed'
      [ -n "${anon_shared_inst_tb:-}" ] || anon_shared_inst_tb="closed"
   else
      ## Tor Browser is not yet available at time of writing for ARM64 and
      ## other architecture.
      ## TODO: reproducible builds
      [ -n "${anon_shared_inst_tb:-}" ] || anon_shared_inst_tb="open"
   fi
else
   ## Compatible with git shallow clone.
   ## Non-redistributable (personal) builds always use 'HEAD' as the trust
   ## root. This is by design: personal builds are created by the developer
   ## on their own machine from their own checkout, so the checkout is
   ## already trusted. Full history-based trust root verification is only
   ## required for redistributable builds.
   [[ -v sq_git_trust_root ]] || sq_git_trust_root="HEAD"

   ## Non-redistributable builds use '--tb open' by default.
   [ -n "${anon_shared_inst_tb:-}" ] || anon_shared_inst_tb="open"

   [ -n "${build_remote_repo_enable:-}" ] || build_remote_repo_enable="false"
   export build_remote_repo_enable
fi
## Export sq_git_trust_root so 'help-steps/git_sanity_test' can read it.
export sq_git_trust_root

## Variable 'anon_shared_inst_tb' only has an effect if package 'tb-updater' is installed.
export anon_shared_inst_tb

## repository-dist-initializer
repository_dist_initializer_setup() {
   [ -n "${binary_build_folder_dist}" ] || error "repository_dist_initializer_setup: variable binary_build_folder_dist missing!"
   [ -n "${CHROOT_FOLDER}" ] || error "repository_dist_initializer_setup: variable CHROOT_FOLDER missing!"

   ## Enable generation of repository-dist config file
   true "INFO: build_remote_repo_enable: $build_remote_repo_enable"
   if [ "${build_remote_repo_enable}" = 'true' ]; then
      printf "%s\n" "${DERIVATIVE_APT_REPOSITORY_OPTS}" | sponge -- "${binary_build_folder_dist}/derivative_apt_repository_opts"

      ## Debugging.
      true "INFO: sudo test"
      $SUDO_TO_ROOT test -d /usr

      $SUDO_TO_ROOT mkdir --parents -- "${CHROOT_FOLDER}/var/lib/repository-dist/"
      $SUDO_TO_ROOT cp -- "${binary_build_folder_dist}/derivative_apt_repository_opts" "${CHROOT_FOLDER}/var/lib/repository-dist/derivative_apt_repository_opts"
   else
      rm --force -- "${binary_build_folder_dist}/derivative_apt_repository_opts"
   fi
}

export dist_build_sources_list_primary

## https://forums.whonix.org/t/kicksecure-etc-hostname-etc-hosts/10919
[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="localhost"

if [ "${dist_build_flavor:-}" = "source" ]; then
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="source"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="source"
fi

if [ "${dist_build_flavor:-}" = "dist-installer-cli" ]; then
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="dist-installer-cli"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="dist-installer-cli"
fi

if [ "${dist_build_flavor:-}" = "whonix-gateway-lxqt" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Gateway"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Gateway-LXQt"
   [ -n "${VMRAM:-}" ] || VMRAM="1280"
   [ -n "${VRAM:-}" ] || VRAM="128"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="gateway"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="LXQt"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="true"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" whonix-gateway-vm-gui-lxqt "
fi

if [ "${dist_build_flavor:-}" = "whonix-gateway-rpi" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Gateway"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Gateway-RPi"
   [ -n "${VMRAM:-}" ] || VMRAM="768"
   [ -n "${VRAM:-}" ] || VRAM="16"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="gateway"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="RPi"
   ## TODO
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="true"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   #[ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" whonix-gateway-rpi "
   error "${red}${bold}dist_build_flavor $dist_build_flavor is not implemented.${reset}"
fi

if [ "${dist_build_flavor:-}" = "whonix-gateway-cli" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Gateway"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Gateway-CLI"
   [ -n "${VMRAM:-}" ] || VMRAM="768"
   [ -n "${VRAM:-}" ] || VRAM="16"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="gateway"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="CLI"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="false"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" whonix-gateway-vm-server "
fi

if [ "${dist_build_flavor:-}" = "whonix-workstation-lxqt" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Workstation"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Workstation-LXQt"
   [ -n "${VMRAM:-}" ] || VMRAM="2048"
   [ -n "${VRAM:-}" ] || VRAM="128"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="workstation"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="LXQt"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="true"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" whonix-workstation-vm-gui-lxqt "
fi

if [ "${dist_build_flavor:-}" = "whonix-workstation-cli" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Workstation"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Workstation-CLI"
   [ -n "${VMRAM:-}" ] || VMRAM="768"
   [ -n "${VRAM:-}" ] || VRAM="16"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="workstation"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="CLI"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="false"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" whonix-workstation-vm-server "
fi

if [ "${dist_build_flavor:-}" = "whonix-custom-workstation" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Custom-Workstation"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Custom-Workstation"
   [ -n "${VMRAM:-}" ] || VMRAM="2048"
   [ -n "${VRAM:-}" ] || VRAM="128"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="custom-workstation"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="CUSTOM"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="false"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   #[ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" none "
fi

if [ "${dist_build_flavor:-}" = "whonix-host-cli" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Host"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Host-CLI"
   [ -n "${VMRAM:-}" ] || VMRAM="768"
   [ -n "${VRAM:-}" ] || VRAM="16"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="whonix-host"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="CLI"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="false"
   [ -n "${dist_build_host_operating_system:-}" ] || dist_build_host_operating_system="true"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   error "${red}${bold}dist_build_flavor $dist_build_flavor is not implemented.${reset}"
fi

if [ "${dist_build_flavor:-}" = "whonix-host-lxqt" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Whonix-Host"
   [ -n "${VMNAME:-}" ] || VMNAME="Whonix-Host-LXQt"
   [ -n "${VMRAM:-}" ] || VMRAM="2048"
   [ -n "${VRAM:-}" ] || VRAM="128"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="whonix-host"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="whonix"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="LXQt"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="true"
   [ -n "${dist_build_host_operating_system:-}" ] || dist_build_host_operating_system="true"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="host"
   ## TODO: Do not hardcode to VirtualBox.
   #[ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" whonix-lxqt-host-virtualbox "
   error "${red}${bold}dist_build_flavor $dist_build_flavor is not implemented.${reset}"
fi

if [ "${dist_build_flavor:-}" = "kicksecure-cli" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Kicksecure"
   [ -n "${VMNAME:-}" ] || VMNAME="Kicksecure-CLI"
   [ -n "${VMRAM:-}" ] || VMRAM="768"
   [ -n "${VRAM:-}" ] || VRAM="16"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="kicksecure"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="CLI"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="false"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="localhost"
   if [ "${dist_build_type:-}" = "vm" ]; then
      [ -n "${dist_build_host_operating_system:-}" ] || dist_build_host_operating_system="false"
      [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="kicksecure"
      [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" kicksecure-vm-server "
   elif [ "${dist_build_type:-}" = "host" ]; then
      [ -n "${dist_build_host_operating_system:-}" ] || dist_build_host_operating_system="true"
      [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="kicksecure-host"
      [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" kicksecure-baremetal-server "
   fi
fi

if [ "${dist_build_flavor:-}" = "kicksecure-lxqt" ]; then
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="Kicksecure"
   [ -n "${VMNAME:-}" ] || VMNAME="Kicksecure-LXQt"
   [ -n "${VMRAM:-}" ] || VMRAM="2048"
   [ -n "${VRAM:-}" ] || VRAM="128"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="kicksecure"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="LXQt"
   [ -n "${dist_build_gui:-}" ] || dist_build_gui="true"
   #[ -n "${dist_build_hostname:-}" ] || dist_build_hostname="localhost"
   if [ "${dist_build_type:-}" = "vm" ]; then
      [ -n "${dist_build_host_operating_system:-}" ] || dist_build_host_operating_system="false"
      [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="kicksecure"
      [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" kicksecure-vm-gui-lxqt "
   elif [ "${dist_build_type:-}" = "host" ]; then
      [ -n "${dist_build_host_operating_system:-}" ] || dist_build_host_operating_system="true"
      [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="kicksecure-host"
      [ -n "${flavor_meta_packages_to_install:-}" ] || flavor_meta_packages_to_install=" kicksecure-baremetal-gui-lxqt "
   fi
fi

## derivative_name_list is used by:
## - help-steps/variables to identify valid derivative names.
## - help-steps/create-local-temp-apt-repo to create one or multiple local APT repositories.
## - help-steps/cleanup-files to clean one or multiple folders.
## - help-steps/unchroot-raw to unmount one or multiple local APT repositories.
## - buildconfig.d/30_pkg_list.conf to create a list of packages to be built.
## - buildconfig.d/25_apt_sources.conf to create a APT URIs.
## - build-steps.d/*_create-debian-packages to iterate through one or multiple package folders.

if [ "${dist_build_source_run:-}" = "true" ]; then
   [ -n "${VMNAME:-}" ] || VMNAME="source"
   [ -n "${SHORT_VMNAME:-}" ] || SHORT_VMNAME="source"
   [ -n "${derivative_name_list:-}" ] || derivative_name_list="kicksecure whonix"
   [ -n "${dist_build_type_long:-}" ] || dist_build_type_long="source"
   [ -n "${dist_build_type_short:-}" ] || dist_build_type_short="source"
   [ -n "${dist_build_desktop:-}" ] || dist_build_desktop="none"
fi

## Some flavor/target combinations do not set 'dist_build_type_long' in the
## per-flavor blocks above -- e.g. the 'kicksecure-cli' / 'kicksecure-lxqt'
## blocks only set it when 'dist_build_type' is 'vm' or 'host', so a
## '--target source' build leaves it unset. Give it a useful value in all
## cases by defaulting to 'dist_build_type_short' (always set by the flavor
## blocks, and equal to 'dist_build_type_long' for kicksecure builds), so
## every consumer can rely on it being set.
[ -n "${dist_build_type_long:-}" ] || dist_build_type_long="${dist_build_type_short:-}"

export dist_build_type_long

export VMNAME VMRAM VRAM

## Used by: developer-meta-files /usr/bin/dm-prepare-release
vm_multiple=false
if [ "${dist_build_type_long:-}" = "workstation" ]; then
   vm_multiple=true
   ## dist_build_desktop could be KDE, CLI, Xfce, LXQt, RPi or CUSTOM
   [ -n "${vm_names_to_be_exported:-}" ] || vm_names_to_be_exported="Whonix-Gateway-${dist_build_desktop:-} Whonix-Workstation-${dist_build_desktop:-}"
fi
if [ "${dist_build_type_long:-}" = "custom-workstation" ]; then
   [ -n "${vm_names_to_be_exported:-}" ] || vm_names_to_be_exported="${VMNAME:-}"
fi
if [ "${dist_build_type_short:-}" = "kicksecure" ]; then
   [ -n "${vm_names_to_be_exported:-}" ] || vm_names_to_be_exported="${VMNAME:-}"
fi
## --flavor source fallback
[ -n "${vm_names_to_be_exported:-}" ] || vm_names_to_be_exported="none"

maybe_disable_dist_build_windows_installer() {
   if [ "${dist_build_source_run:-}" = "true" ]; then
      true "${BASH_SOURCE[0]} INFO: dist_build_source_run set to true, skipping ${FUNCNAME[0]}, ok."
   else
      true "${BASH_SOURCE[0]} INFO: dist_build_source_run is not set to true, therefore: dist_build_windows_installer=false"
      dist_build_windows_installer=false
   fi
}

## TODO: refactor confusingly named variables:
## dist_server_with_upload_location_list
## dist_server_with_upload_location_list_list

if [ "${dist_build_type_short:-}" = "kicksecure" ]; then
   [ -n "${derivative_name_list:-}" ] || derivative_name_list="kicksecure"
   [ -n "${dist_build_type_short_pretty:-}" ] || dist_build_type_short_pretty="Kicksecure"
   [ -n "${dist_server_with_upload_location_list_list:-}" ] || dist_server_with_upload_location_list_list="kicksecure.com"
   [ -n "${project_clearnet:-}" ] || project_clearnet="kicksecure.com"
   true "${BASH_SOURCE[0]} INFO: dist_build_type_short is 'kicksecure'. Build Whonix Windows Installer only for Whonix LXQt for VirtualBox. Therefore: maybe_disable_dist_build_windows_installer"
   maybe_disable_dist_build_windows_installer
elif [ "${dist_build_type_short:-}" = "whonix" ]; then
   [ -n "${derivative_name_list:-}" ] || derivative_name_list="kicksecure whonix"
   [ -n "${dist_build_type_short_pretty:-}" ] || dist_build_type_short_pretty="Whonix"
   [ -n "${dist_server_with_upload_location_list_list:-}" ] || dist_server_with_upload_location_list_list="whonix.org"
   [ -n "${project_clearnet:-}" ] || project_clearnet="whonix.org"
elif [ "${dist_build_type_short:-}" = "dist-installer-cli" ]; then
   [ -n "${derivative_name_list:-}" ] || derivative_name_list="kicksecure whonix"
   [ -n "${dist_build_type_short_pretty:-}" ] || dist_build_type_short_pretty="dist-installer-cli"
   [ -n "${dist_server_with_upload_location_list_list:-}" ] || dist_server_with_upload_location_list_list="kicksecure.com whonix.org"
   [ -n "${project_clearnet:-}" ] || project_clearnet="kicksecure.com"
   true "${BASH_SOURCE[0]} INFO: dist_build_type_short is 'dist-installer-cli'. Build Whonix Windows Installer only for Whonix LXQt for VirtualBox. Therefore: maybe_disable_dist_build_windows_installer"
   maybe_disable_dist_build_windows_installer
   ## Hardcoded.
   VMNAME=dist-installer-cli
elif [ "${dist_build_type_short:-}" = "source" ]; then
   ## --flavor source / --target source path. Set the same defaults the
   ## kicksecure / whonix / dist-installer-cli branches set, so later
   ## consumers (e.g. dist_server_with_upload_location_list,
   ## derivative_name_list usage) don't trip on unset variables. The
   ## values match dist-installer-cli, which is also a non-flavor-
   ## specific multi-derivative build.
   [ -n "${derivative_name_list:-}" ]         || derivative_name_list="kicksecure whonix"
   [ -n "${dist_build_type_short_pretty:-}" ] || dist_build_type_short_pretty="source"
   [ -n "${dist_server_with_upload_location_list_list:-}" ] || dist_server_with_upload_location_list_list="kicksecure.com whonix.org"
   [ -n "${project_clearnet:-}" ]             || project_clearnet="kicksecure.com"
else
   error "unknown dist_build_type_short!"
fi

## example dist_build_desktop_lowercase:
## lxqt
## Bash's lowercase expansion ',,' refuses an unset var under
## nounset. Take the value through a defaulted intermediate.
dist_build_desktop_safe="${dist_build_desktop:-}"
[ -n "${dist_build_desktop_lowercase:-}" ] || dist_build_desktop_lowercase="${dist_build_desktop_safe,,}"
unset dist_build_desktop_safe

if [ ! "${dist_build_desktop_lowercase:-}" = "lxqt" ] ; then
   true "${BASH_SOURCE[0]} INFO: dist_build_desktop_lowercase is '$dist_build_desktop_lowercase'. Not 'lxqt'. Build Whonix Windows Installer only for Whonix LXQt for VirtualBox. Therefore: maybe_disable_dist_build_windows_installer"
   maybe_disable_dist_build_windows_installer
fi

## vmsize 100 GB does not matter because grml-debootstrap creates sparse files,
## i.e. only space, which gets actually filled up, will take disk space.

if [ "${build_dry_run:-}" = "true" ]; then
   [ -n "${VMSIZE:-}" ] || VMSIZE="1M"
fi
[ -n "${VMSIZE:-}" ] || VMSIZE="100G"
export VMSIZE

## Disk identifier for grml-debootstrap.
## https://github.com/grml/grml-debootstrap/pull/28
[ -n "${DISK_IDENTIFIER:-}" ] || DISK_IDENTIFIER='26ada0c0-1165-4098-884d-aafd2220c2c6'
export DISK_IDENTIFIER

if [ "${dist_build_install_to_root:-}" = "true" ]; then
   [ -n "${CHROOT_FOLDER:-}" ] || CHROOT_FOLDER=""
else
   [ -n "${CHROOT_FOLDER:-}" ] || CHROOT_FOLDER="${binary_build_folder_dist:-}/${VMNAME:-}_image"
fi
export CHROOT_FOLDER

true "${BASH_SOURCE[0]} INFO: CHROOT_FOLDER: $CHROOT_FOLDER"

## {{{ initramfs

## binutils dmsetup:
##     https://packages.debian.org/bullseye/dmsetup lists 'Recommends:' which
##     are actually required for a functional boot process.
## pigz:
##     maybe not required but useful.
## dracut-config-rescue:
##     Not actually doing anything.
##     https://www.kicksecure.com/wiki/Dev/todo#investigate_dracut-config-rescue
## dracut-config-generic:
##     Actively detrimental. Introduces a file at
##     /usr/lib/dracut/dracut.conf.d/50-generic.conf which sets 'hostonly="no",
##     overriding /etc/dracut.conf.d/30-dist-base-files.conf.
[ -n "${BUILD_INITRAMFS_DRACUT:-}" ] || BUILD_INITRAMFS_DRACUT="dracut dracut-live binutils dmsetup pigz"

[ -n "${BUILD_INITRAMFS_PKGS:-}" ] || BUILD_INITRAMFS_PKGS="$BUILD_INITRAMFS_DRACUT"

if printf "%s\n" "$BUILD_INITRAMFS_PKGS" | grep --quiet -- "dracut" ; then
   ## INITRD_GENERATOR_OPTS gets read by grml-debootstrap and passed to dracut and is also
   ## read by build-steps.d/*_install-packages
   ##
   ## Worked but issue:
   ## https://forums.kicksecure.com/t/iso-error-message-during-boot-mount-sysroot-special-device-liveos-rootfs-does-not-exist/418
   [ -n "${INITRD_GENERATOR_OPTS:-}" ] || INITRD_GENERATOR_OPTS='--add dmsquash-live --add overlayfs --add bash'
   export INITRD_GENERATOR_OPTS
fi

source_dracut_disable_config_snippet="$source_code_folder_dist/packages/kicksecure/initializer-dist/usr/share/initializer-dist/dracut/90-derivative-maker-build.conf"
target_dracut_disable_config_snippet="$CHROOT_FOLDER/etc/dracut.conf.d/90-derivative-maker-build.conf"

source_dkms_suppress_mok_snippet="$source_code_folder_dist/packages/kicksecure/initializer-dist/usr/share/initializer-dist/dkms/90-derivative-maker-build.conf"
target_dkms_suppress_mok_snippet="$CHROOT_FOLDER/etc/dkms/framework.conf.d/90-derivative-maker-build.conf"

true "${cyan}${BASH_SOURCE[0]} INFO: BUILD_INITRAMFS_PKGS (--initramfs): $BUILD_INITRAMFS_PKGS${reset}"

## }}}

if [ "${VMNAME:-}" = "unknown" ]; then
   true "variables ERROR: VMNAME is unknown. Please report this bug!"
   exit 1
fi

if [ "${dist_build_source_run:-}" = "true" ]; then
   true "${BASH_SOURCE[0]} INFO: Internal run."
else
   true "${BASH_SOURCE[0]} INFO: VMNAME is ${VMNAME:-}"
fi

if [ "${VMNAME:-}" = "" ]; then
   MSG="${red}${bold}variables ERROR: VMNAME is empty! Please report this bug!${reset}"
   error "$MSG"
fi

[ -n "${XZ_OPT:-}" ] || XZ_OPT="--threads=8"
export XZ_OPT

if [ "${dist_build_unsafe_io:-}" = "true" ]; then
   eatmydata_install="true"
   aptgetopt_add "Dpkg::Options::=--force-unsafe-io"
   aptgetopt_conf_add "Dpkg::Options:: \"--force-unsafe-io\";"
   ## Resolve the host multiarch triplet deterministically rather than
   ## globbing '/usr/lib/*-linux-gnu/libeatmydata.so'. On a multiarch host
   ## (e.g. amd64 with i386 foreign), the glob returns both paths; the
   ## first is chosen by alphabetic order which can be the wrong ABI,
   ## causing ld.so to reject the preload or silently skip it.
   libeatmydata_multiarch=""
   if command -v dpkg-architecture >/dev/null 2>&1 ; then
      libeatmydata_multiarch="$(dpkg-architecture --query=DEB_HOST_MULTIARCH 2>/dev/null)"
   fi
   if [ -z "${libeatmydata_multiarch:-}" ] && command -v gcc >/dev/null 2>&1 ; then
      libeatmydata_multiarch="$(gcc -print-multiarch 2>/dev/null)"
   fi
   libeatmydata_file=""
   if [ -n "${libeatmydata_multiarch:-}" ] && test -f "/usr/lib/${libeatmydata_multiarch}/libeatmydata.so" ; then
      libeatmydata_file="/usr/lib/${libeatmydata_multiarch}/libeatmydata.so"
   fi
   if [ -n "${libeatmydata_file:-}" ] && test -f "$libeatmydata_file" ; then
      true "INFO: eatmydata installed, preloading."
      if [ -z "${LD_PRELOAD:-}" ]; then
         LD_PRELOAD="$libeatmydata_file"
      else
         LD_PRELOAD="${LD_PRELOAD}:${libeatmydata_file}"
      fi
      export LD_PRELOAD
      ## Inject LD_PRELOAD as a sudo command-line environment assignment
      ## (not just via --preserve-env) so that sudo actually passes it
      ## into the child process. 'export LD_PRELOAD' on the parent is
      ## not enough: by default sudo strips LD_* variables from the
      ## environment for security reasons. 'sudo FOO=bar cmd' runs 'cmd'
      ## with FOO=bar set directly, bypassing the strip. LD_PRELOAD is
      ## additionally listed in 'env_vars_keep_list' so that
      ## '--preserve-env=...' (appended to SUDO_TO_ROOT further below)
      ## keeps it alive for any nested sudo invocations.
      SUDO_TO_ROOT="$SUDO_TO_ROOT LD_PRELOAD=$LD_PRELOAD"
   else
      true "INFO: eatmydata not yet installed, not yet preloading."
   fi
   ## Shadow the 'sync' command with a no-op function. Bash resolves
   ## function names before external commands, so in this shell and all
   ## its subshells 'sync' becomes a no-op. The goal of '--unsafe-io' is
   ## to speed up the build by letting the kernel defer writes; calling
   ## '/bin/sync' would defeat that. This only affects processes that
   ## inherit this shell's function table (i.e. subshells of 'bash'),
   ## not spawned binaries that directly exec '/bin/sync' themselves.
   sync() {
      true "${FUNCNAME[0]}: Not running ${FUNCNAME[0]}, because using '--unsafe-io true'."
   }
fi

## use approx by default
## required for --connection clearnet
## Port 9977 defined by approx/approx.conf
## REPO_PROXY is no longer used now that we're using approx.
#[ -n "${REPO_PROXY:-}" ] || REPO_PROXY="http://127.0.0.1:9977"
[ -n "${APPROX_PROXY_ENABLE:-}" ] || APPROX_PROXY_ENABLE='yes'

[ -v DEBOOTSTRAP_PREFIX ] || DEBOOTSTRAP_PREFIX=""
[ -v COWBUILDER_PREFIX ] || COWBUILDER_PREFIX=""
[ -v LIVEBUILD_PREFIX ] || LIVEBUILD_PREFIX=""

if [ "${APPROX_PROXY_ENABLE}" = "yes" ]; then
   ## apt parameters must be without quotes
   ## apt parameters must be without equals sign
   ## apt parameters must be without colon sign
   ## apt config file must be with quotes
   ## apt config file must be without equals sign
   ## apt config file must be trailing colon sign

   ## Obsolete now that we're using approx.
   #aptgetopt_add "Acquire::http::Proxy=${REPO_PROXY}"
   #aptgetopt_conf_add "Acquire::http::Proxy \"${REPO_PROXY}\";"

   #aptgetopt_add "Acquire::https::Proxy=${REPO_PROXY}"
   #aptgetopt_conf_add "Acquire::https::Proxy \"${REPO_PROXY}\";"

   ## https://forums.whonix.org/t/derivative-maker-fails-with-onion-sources/20200/8
   #aptgetopt_add "Acquire::tor::Proxy=${REPO_PROXY}"
   #aptgetopt_conf_add "Acquire::tor::Proxy \"${REPO_PROXY}\";"

   aptgetopt_add "Acquire::BlockDotOnion=false"
   aptgetopt_conf_add "Acquire::BlockDotOnion \"false\";"

   #DEBOOTSTRAP_PREFIX+=" env http_proxy=${REPO_PROXY} https_proxy=${REPO_PROXY} ALL_PROXY=${REPO_PROXY}"
   #COWBUILDER_PREFIX+=" env http_proxy=${REPO_PROXY} https_proxy=${REPO_PROXY} ALL_PROXY=${REPO_PROXY}"
   #LIVEBUILD_PREFIX+=" env http_proxy=${REPO_PROXY} https_proxy=${REPO_PROXY} ALL_PROXY=${REPO_PROXY}"
fi

## Help update-torbrowser and other software find the Tor server if using
## '--connection tor'.
if [ "${dist_build_sources_clearnet_or_onion:-}" = 'onion' ]; then
   if [ -z "${CURL_PROXY:-}" ]; then
      ## TODO: Possibly allow customizing the server and port for this?
      ## HARDCODED.
      CURL_PROXY='--proxy socks5h://127.0.0.1:9050'
      export CURL_PROXY
   fi
fi

## CURL_RESUME resumes a partially-downloaded file (curl '--continue-at -',
## the same value tb-updater uses for '--resume'). Default it and export so
## build steps that reference ${CURL_RESUME:-} resume large downloads; an
## inherited value still wins.
[ -n "${CURL_RESUME:-}" ] || CURL_RESUME="--continue-at -"
export CURL_RESUME

## Export COWBUILDER_PREFIX so it is available in make-helper.bsh.
export COWBUILDER_PREFIX

## Variables removed from the environment before entering a target chroot
## (chroot_run). These name host-only state that must not leak into the built
## image: TEMP/TMPDIR are host temp dirs, and HELPER_SCRIPTS_PATH points at the
## host build tree -- inside the chroot the INSTALLED helper-scripts must be
## used, and package postinsts source "${HELPER_SCRIPTS_PATH:-}/..." expecting
## it empty so the installed '/usr/libexec/helper-scripts' path is used.
chroot_env_vars_var_remove_list="
   TEMP
   TEMPDIR
   TMP
   TMPDIR
   HELPER_SCRIPTS_PATH
"

chroot_remove_env_list=""
for chroot_env_vars_var_remove_item in $chroot_env_vars_var_remove_list ; do
   if [ "${chroot_remove_env_list:-}" = "" ]; then
      chroot_remove_env_list="--unset $chroot_env_vars_var_remove_item"
   else
      chroot_remove_env_list+=" --unset $chroot_env_vars_var_remove_item"
   fi
done
unset chroot_env_vars_var_remove_item

if [ "${dist_build_install_to_root:-}" = "true" ]; then
   chroot_run() {
      $SUDO_TO_ROOT "$@"
   }
else
   chroot_run() {
      $SUDO_TO_ROOT env $chroot_remove_env_list -- chroot -- "$CHROOT_FOLDER" "$@"

      #$SUDO_TO_ROOT env $chroot_remove_env_list systemd-nspawn --directory="$CHROOT_FOLDER"
   }
fi

if [ "${no_git:-}" = "1" ]; then
   [ -n "${git_bin:-}" ] || git_bin="true"
else
   [ -n "${git_bin:-}" ] || git_bin="git"
fi

## Otherwise errors out because there is a folder grml-debootstarp in the root source folder.
## Cannot use `which grml-debootstrap` because that would fail before grml-debootstrap gets installed.
#[ -n "${dist_build_grml_bin:-}" ] || dist_build_grml_bin="$(which grml-debootstrap)"
[ -n "${dist_build_grml_bin:-}" ] || dist_build_grml_bin="/usr/sbin/grml-debootstrap"

if [ -z "${rsync_cmd:-}" ]; then
   if [ "${build_dry_run:-}" = "true" ]; then
      ## '--dry-run true' is documented as "useful for debugging
      ## dm-prepare-release"; the artifacts being uploaded are
      ## placeholders, so mock rsync. This is the same mock
      ## 'help-steps/pre' applies for '$CI=true' but here it also
      ## fires for 'build_dry_run=true' (parse-cmd sets that flag
      ## above this line, so we can read it now).
      rsync_cmd="echo simulate-only rsync"
   else
      rsync_cmd="rsync"
   fi
fi

## TODO: hardlinks: rsync error Internal hashtable error: illegal key supplied
## --hard-links \
if [ "${rsync_opts:-}" = "" ]; then
   rsync_opts="\
   --links \
   --safe-links \
   --owner \
   --group \
   --chown=root:www-data \
   --chmod=uog+r \
   --times \
   --sparse \
   --partial \
   --progress \
   --verbose"
fi

if [ "${build_dry_run:-}" = "true" ]; then
   rsync_opts="$rsync_opts --dry-run"
fi

## TODO: auto retry function error handler trap lacks variables
## such as variable DIST_APTGETOPT.
## Would miss out on security related APT configuration options:
## apt-get --error-on=any / -o APT::Update::Error-Mode=any
[ -n "${dist_build_auto_retry:-}" ] || dist_build_auto_retry="0"

[ -n "${dist_build_wait_auto_retry:-}" ] || dist_build_wait_auto_retry="5"

## Sanity Test.
command -v "$git_bin" >/dev/null

[ -n "${DEB_INSTALL_FOLDER:-}" ] || DEB_INSTALL_FOLDER="mnt/initialdeb"
export DEB_INSTALL_FOLDER

[ -n "${EMPTY_DIR:-}" ] || EMPTY_DIR="/tmp/empty"
export EMPTY_DIR

[ -n "${derivative_maker:-}" ] || derivative_maker=true
export derivative_maker

## {{{ dist_build_on_operating_system

if [ "${dist_build_on_operating_system_detect_skip:-}" = "1" ]; then
   true "Probably run by prepare-build-machine script. lsb_release may not yet installed. Skipping setting dist_build_on_operating_system."
else
   ## Let's find out on which host operating system the build script is running.
   [ -n "${dist_build_on_operating_system:-}" ] || dist_build_on_operating_system="$(lsb_release --short --id)"
   ## Converting dist_build_on_operating_system to lower case to make later if comparison easier.
   dist_build_on_operating_system="${dist_build_on_operating_system,,}"
   ## Example dist_build_on_operating_system: debian
   ## Example dist_build_on_operating_system: ubuntu
   ## XXX
   if [ "${dist_build_on_operating_system:-}" = "whonix" ]; then
      dist_build_on_operating_system="debian"
   fi
fi

## }}}

## {{{ dist_build_current_git_head

## Get the ref (branch or commit) that we're currently at.
##
## Unconditionally recompute from 'git rev-parse HEAD'. There isn't a
## plausible situation where being able to customize this is useful. (It also
## technically allow an attacker with the ability to modify the environment to
## fool derivative-update into checking out the wrong commit, but an attacker
## that can modify the environment can likely do much worse things, so this
## isn't a real concern.)
##
## NOTE: Cannot use end-of-options ("--").
dist_build_current_git_head="$($git_bin rev-parse HEAD)" || error 'Cannot get head commit ID!'

#printf "%s\n" "dist_build_current_git_head: $dist_build_current_git_head"

## }}}

## {{{ dist_build_version

if [ "${dist_build_version:-}" = "" ]; then
   ## --always because travis-ci does not fetch tags.
   ## Examples dist_build_version:
   ## 10-13-g20e1b49ff27053784e3e9e163dfd4c98dced73f5
   ## 10.0.0.0.3-13-g20e1b49ff27053784e3e9e163dfd4c98dced73f5-developers-only
   dist_build_version="$($git_bin describe --always --abbrev=1000000000)"
   true "${cyan}INFO: Variable dist_build_version was unset. Auto detected. Set to: dist_build_version=${dist_build_version}${reset}"
else
   true "${cyan}INFO: Variable dist_build_version was already set to: dist_build_version=${dist_build_version}${reset}"
fi

## Strip channel suffixes ('-developers-only', '-testers-only', '-stable')
## that may be attached to the git tag so that 'dist_build_version' is a
## clean version string regardless of which release channel we are on.
## Single sed invocation with alternation instead of three pipelines.
dist_build_version="$(printf "%s\n" "$dist_build_version" | sed -E "s#-(developers-only|testers-only|stable)##g")"

## Using `export`, so 70_log_build_version can read it.
export dist_build_version

## Example dist_build_version:
## 10.0.0.0.3-13-g20e1b49ff27053784e3e9e163dfd4c98dced73f5

## }}}

set_default_variable() {
  local var_name var_content
  var_name="$1"
  var_content="$2"

  if [ -z "${!var_name:-}" ]; then
    #printf "%s\n" "INFO: Variable '$var_name' is already set? No. - Setting: $var_name='$var_content'"
    ## Use 'printf -v' instead of 'eval' so single quotes or other shell
    ## metacharacters inside "$var_content" are stored literally and cannot
    ## be reinterpreted by the shell parser.
    printf -v "$var_name" '%s' "$var_content"
    return 0
  fi
  #printf "%s\n" "INFO: Variable '$var_name' is already set? Yes. - Already: $var_name='${!var_name}'"
}

if [ "${dist_build_source_archive:-}" = "true" ]; then
    set_default_variable dist_unified_name "derivative-maker-source"
elif [ "${dist_build_type_short:-}" = "kicksecure" ]; then
    set_default_variable dist_unified_name "Kicksecure-${dist_build_desktop}"
elif [ "${dist_build_type_short:-}" = "whonix" ]; then
    set_default_variable dist_unified_name "Whonix-${dist_build_desktop}"
elif [ "${dist_build_type_short:-}" = "dist-installer-cli" ]; then
    set_default_variable dist_unified_name "dist-installer-cli"
elif [ "${dist_build_type_short:-}" = "source" ]; then
    set_default_variable dist_unified_name "source"
else
    error "dist_unified_name: unhandled dist_build_type_short '$dist_build_type_short'"
fi

set_default_variable dist_binary_build_folder "${binary_build_folder_dist}/${dist_build_version}"

set_default_variable binary_image_raw_file "${dist_binary_build_folder}/${SHORT_VMNAME}-${dist_build_desktop}-${dist_build_version}.${target_architecture_pretty_name}.raw"
set_default_variable binary_image_qcow2_file "${dist_binary_build_folder}/${SHORT_VMNAME}-${dist_build_desktop}-${dist_build_version}.${target_architecture_pretty_name}.qcow2"

## Working folder for the intermediate VirtualBox '.vdi' created by
## 'build-steps.d/*_create-vbox-vm'. Deliberately on host '/rw'-backed storage
## (a sibling of '$binary_build_folder_dist' under '$HOMEVAR') rather than the
## chroot's 'dm-vbox-temp' home, so the multi-gigabyte disk stays off the small
## pbuilder chroot filesystem and out of 'base.cow' (only the tiny VirtualBox
## registry, referencing this absolute path, is persisted via
## '--save-after-login').
##
## It is bind-mounted into the chroot as its OWN mount (see the 'bindmounts' in
## 'build-steps.d/*_create-vbox-vm' and 'dm-prepare-release') rather than being
## nested under the bind-mounted '$binary_build_folder_dist'. That matters for
## traversal: '$binary_build_folder_dist' is the operator's build folder (mode
## '0770', owned by the build user), which the unprivileged chroot account
## 'dm-vbox-temp' cannot traverse. As its own mount its chroot parent is
## '$HOMEVAR', a pbuilder-created 'root:root 0755' mountpoint that
## 'dm-vbox-temp' can traverse to reach the (chown'ed) disk. The same mount is
## re-established in the export step, so the registry's absolute medium path
## resolves in both.
set_default_variable binary_image_vdi_dir "${HOMEVAR}/derivative-binary_vbox-workdir"

## Like '$binary_build_folder_dist' / '$source_code_folder_dist' (checked
## earlier), '$binary_image_vdi_dir' is passed via 'cowbuilder --bindmounts',
## which pbuilder word-splits on whitespace, so a path containing whitespace
## silently breaks the bind-mount. It is checked here rather than in that early
## loop because it is only defined at this point. Fail early with a clear
## message instead of late with a cryptic pbuilder / VirtualBox error.
case "$binary_image_vdi_dir" in
   *[[:space:]]*)
      error "binary_image_vdi_dir must not contain whitespace (it is bind-mounted into the cowbuilder chroot and pbuilder word-splits BINDMOUNTS): '$binary_image_vdi_dir'"
      ;;
esac

## NOTE: 'binary_image_{raw,qcow2}_file_for_unified' reads like a list but
## is singular by construction. For a unified (multi-VM) build such as
## Whonix, 'vm_names_to_be_exported' contains exactly two names (Gateway
## and Workstation); the 'continue' below skips the one matching the
## current 'dist_build_type_long', leaving exactly one "other" VM whose
## image path is stored here. Consumers (dm-prepare-release,
## 1200_prepare-build-machine) treat it as a single path - e.g. the
## error message reads "missing other VM for unified builds". Do not
## convert this to an array without updating those callers.
for vm_name_item_to_be_exported in $vm_names_to_be_exported ; do
   ## Compare case-insensitively but as exact substrings, not regex, to
   ## avoid regex metacharacters in 'dist_build_type_long' matching
   ## unintended VM names.
   vm_name_item_lower="${vm_name_item_to_be_exported,,}"
   dist_build_type_long_lower="${dist_build_type_long,,}"
   case "$vm_name_item_lower" in
      *"$dist_build_type_long_lower"*)
         continue
         ;;
   esac
   set_default_variable binary_image_raw_file_for_unified "${dist_binary_build_folder}/${vm_name_item_to_be_exported}-${dist_build_version}.${target_architecture_pretty_name}.raw"
   set_default_variable binary_image_qcow2_file_for_unified "${dist_binary_build_folder}/${vm_name_item_to_be_exported}-${dist_build_version}.${target_architecture_pretty_name}.qcow2"
done
unset vm_name_item_lower dist_build_type_long_lower

set_default_variable binary_image_ova_file "${dist_binary_build_folder}/${dist_unified_name}-${dist_build_version}.${target_architecture_pretty_name}.ova"
set_default_variable binary_image_iso_file "${dist_binary_build_folder}/${SHORT_VMNAME}-${dist_build_desktop}-${dist_build_version}.${target_architecture_pretty_name}.iso"
set_default_variable binary_image_windows_installer_file "${dist_binary_build_folder}/${dist_unified_name}-${dist_build_version}.exe"
set_default_variable libvirt_target_raw_xz_archive_file "${dist_binary_build_folder}/${dist_unified_name}-${dist_build_version}.${target_architecture_pretty_name}.raw.libvirt.xz"
set_default_variable libvirt_target_qcow2_xz_archive_file "${dist_binary_build_folder}/${dist_unified_name}-${dist_build_version}.${target_architecture_pretty_name}.qcow2.libvirt.xz"
set_default_variable source_archive_xz_file "${dist_binary_build_folder}/${dist_unified_name}-${dist_build_version}.xz"

set_default_variable binary_image_text "${dist_binary_build_folder}/${SHORT_VMNAME}-${dist_build_desktop}-${dist_build_version}.text"

## virtualbox-installer / dist-installer-cli
set_default_variable binary_image_installer_dist_source "${source_code_folder_dist}/packages/kicksecure/usability-misc/usr/share/usability-misc/dist-installer-cli-standalone"
set_default_variable binary_image_installer_dist_file "${dist_binary_build_folder}/dist-installer-cli"

set_default_variable libvirt_source_kvm_file "${source_code_folder_dist}/packages/kicksecure/libvirt-dist/usr/share/libvirt-dist/xml/${SHORT_VMNAME}.xml"
set_default_variable libvirt_target_kvm_file "${dist_binary_build_folder}/${SHORT_VMNAME}.xml"

set_default_variable source_utm_file "${source_code_folder_dist}/packages/kicksecure/libvirt-dist/usr/share/utm/${SHORT_VMNAME}.plist.template"
set_default_variable binary_image_utm_folder "${dist_binary_build_folder}/${SHORT_VMNAME}-${dist_build_desktop}.utm"

set_default_variable libvirt_source_network_file_external "${source_code_folder_dist}/packages/kicksecure/libvirt-dist/usr/share/libvirt-dist/xml/Whonix-External.xml"
set_default_variable libvirt_target_network_file_external "${dist_binary_build_folder}/Whonix_external_network.xml"

set_default_variable libvirt_source_network_file_internal "${source_code_folder_dist}/packages/kicksecure/libvirt-dist/usr/share/libvirt-dist/xml/Whonix-Internal.xml"
set_default_variable libvirt_target_network_file_internal "${dist_binary_build_folder}/Whonix_internal_network.xml"

# TODO: broken
set_default_variable copy_vms_into_raw_file_one "${dist_binary_build_folder}/Whonix-Gateway-${dist_build_desktop}-${dist_build_version}.qcow2"
set_default_variable copy_vms_into_raw_file_two "${dist_binary_build_folder}/Whonix-Workstation-${dist_build_desktop}-${dist_build_version}.qcow2"

## Sanity test. dm-prepare-release uses 'rm --force --recursive'.
## XXX: hardcoded derivative-binary
if ! printf "%s\n" "$dist_binary_build_folder" | grep --quiet --fixed-strings -- "derivative-binary" ; then
   printf "%s\n" "$0: FAIL: wrong folder: dist_binary_build_folder: $dist_binary_build_folder"
   exit 1
fi

mkdir --parents -- "${dist_binary_build_folder}"

## {{{

## proper whitespace handling
## https://www.whonix.org/wiki/Dev/bash
unset dist_build_files_to_upload
declare -a dist_build_files_to_upload

if [ "${dist_build_virtualbox:-}" = "true" ]; then
   [ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="root"
   [ -n "${dist_server_with_upload_location_list:-}" ] || dist_server_with_upload_location_list="${ssh_uploader_account}@${dist_server_with_upload_location_list_list}:/var/rsync/ova"

   dist_build_files_to_upload+=("$binary_image_ova_file")
fi

if [ "${dist_build_iso:-}" = "true" ]; then
   [ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="root"
   [ -n "${dist_server_with_upload_location_list:-}" ] || dist_server_with_upload_location_list="${ssh_uploader_account}@${dist_server_with_upload_location_list_list}:/var/rsync/iso"

   dist_build_files_to_upload+=("$binary_image_iso_file")
fi

if [ "${dist_build_qcow2:-}" = "true" ]; then
   #[ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="hulahoop"
   [ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="root"
   [ -n "${dist_server_with_upload_location_list:-}" ] || dist_server_with_upload_location_list="${ssh_uploader_account}@${dist_server_with_upload_location_list_list}:/var/rsync/libvirt"

   dist_build_files_to_upload+=("$libvirt_target_qcow2_xz_archive_file")
fi

## Default this target flag at its source so consumers (notably
## dm-upload-images) can read it bare under 'nounset'; parse-cmd sets it
## 'true' only for a '--target installer-dist' build.
[ -n "${dist_build_installer_dist:-}" ] || dist_build_installer_dist="false"
export dist_build_installer_dist

if [ "${dist_build_installer_dist}" = "true" ]; then
   [ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="root"
   [ -n "${dist_server_with_upload_location_list:-}" ] || dist_server_with_upload_location_list="
${ssh_uploader_account}@kicksecure.com:/var/rsync/dist-installer-cli
${ssh_uploader_account}@whonix.org:/var/rsync/dist-installer-cli
"

   dist_build_files_to_upload+=("$binary_image_installer_dist_file")
fi

if [ "${dist_build_windows_installer:-}" = "true" ]; then
   [ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="root"
   [ -n "${dist_server_with_upload_location_list:-}" ] || dist_server_with_upload_location_list="${ssh_uploader_account}@${dist_server_with_upload_location_list_list}:/var/rsync/windows"

   dist_build_files_to_upload+=("$binary_image_windows_installer_file")
fi

if [ "${dist_build_source_archive:-}" = "true" ]; then
   [ -n "${ssh_uploader_account:-}" ] || ssh_uploader_account="root"
   [ -n "${dist_server_with_upload_location_list:-}" ] || dist_server_with_upload_location_list="${ssh_uploader_account}@${dist_server_with_upload_location_list_list}:/var/rsync/source"

   dist_build_files_to_upload+=("$source_archive_xz_file")
fi

## }}}

## {{{ buildconfig.d

[ -n "${dist_build_config_dirs_list:-}" ] || dist_build_config_dirs_list="${source_code_folder_dist}/buildconfig.d /etc/buildconfig-dist.d ${HOMEVAR}/buildconfig.d"

dist_build_source_config_dir() {
   true "${cyan}INFO: Checking if dist_build_config_dir_item $dist_build_config_dir_item exists...${reset}"
   if [ -d "$1" ]; then
      true "${cyan}INFO: Parsing $1...${reset}"
      shopt -s nullglob
      local i
      for i in "$1/"*".conf"; do
         bash -n -- "$i"
         source "$i"
      done
   else
      true "${cyan}INFO: Skipping $1 because it does not exist.${reset}"
   fi
}

for dist_build_config_dir_item in $dist_build_config_dirs_list; do
   dist_build_source_config_dir "$dist_build_config_dir_item"
done
unset dist_build_config_dir_item

if [ ! "${dist_build_custom_config_dir:-}" = "" ]; then
   dist_build_source_config_dir "$dist_build_custom_config_dir"
fi

if [ ! "${dist_build_conf_file:-}" = "" ]; then
   if [ -f "$dist_build_conf_file" ]; then
      bash -n -- "$dist_build_conf_file"
      source "$dist_build_conf_file"
   fi
fi

## }}}

## {{{ sign-and-tag toggle and signature-verification escape
##
## Secure defaults; no auto-detection. Placed after buildconfig.d sourcing so
## an explicit value (a ${HOMEVAR}/buildconfig.d snippet, env, or the
## --sign-and-tag / --allow-unsigned options) always wins over the defaults.

## dist_build_sign_and_tag: whether 'help-steps/sign-and-tag' signs and tags
## HEAD and every submodule. Default 'true'. Set 'false' for local work where
## the submodule-gitlink re-bumping that re-signing causes is unwanted (it
## rewrites submodule HEADs and re-commits the parent gitlinks, dirtying the
## branch).
[ -v dist_build_sign_and_tag ] || dist_build_sign_and_tag="true"
export dist_build_sign_and_tag

## dist_build_ignore_unsigned: signature-skip flag consumed by
## help-steps/git_sanity_test, alongside dist_build_ignore_untagged /
## dist_build_ignore_uncommitted. Default 'false' (verify). Set 'true' only
## when deliberately building from unsigned commits; pair it with
## dist_build_sign_and_tag=false, since not signing means signatures cannot be
## verified.
[ -v dist_build_ignore_unsigned ] || dist_build_ignore_unsigned="false"
export dist_build_ignore_unsigned

## dist_build_script_skip_package_install: optional whitespace-separated list of
## packages a custom-config build wants skipped at install time (see
## build-steps.d/*_install-packages).
[ -v dist_build_script_skip_package_install ] || dist_build_script_skip_package_install=""
export dist_build_script_skip_package_install

## install_package_list: optional whitespace-separated extra-package lists a
## custom-config build may set (see build-steps.d/*_install-packages).
[ -v install_package_list ] || install_package_list=""
export install_package_list

## flavor_meta_packages_to_install: The core metapackage(s) for an image (this
## pulls in most of a system's packages).
[ -v flavor_meta_packages_to_install ] || flavor_meta_packages_to_install=""
export flavor_meta_packages_to_install

## }}}

## {{{ OpenPGP policy + cert paths
##
## Layered the same way as buildconfig.d/*.conf via
## dist_build_config_dirs_list; later entries override earlier
## ones. Written by signing-key-create, read by sign-and-tag.
[ -n "${openpgp_policy_filename:-}" ] || openpgp_policy_filename='openpgp-policy.toml'
[ -n "${openpgp_cert_filename:-}" ]   || openpgp_cert_filename='openpgp-cert.pem'

## Shipped in-tree (upstream maintainer keys only, no local cert).
[ -n "${openpgp_policy_file_project:-}" ] || openpgp_policy_file_project="${source_code_folder_dist}/buildconfig.d/${openpgp_policy_filename}"

## signing-key-create writes to the highest-precedence (last) entry.
[ -n "${openpgp_policy_dir_user:-}" ]  || openpgp_policy_dir_user="${dist_build_config_dirs_list##* }"
[ -n "${openpgp_policy_file_user:-}" ] || openpgp_policy_file_user="${openpgp_policy_dir_user}/${openpgp_policy_filename}"
[ -n "${openpgp_cert_file_user:-}" ]   || openpgp_cert_file_user="${openpgp_policy_dir_user}/${openpgp_cert_filename}"

## Walk dist_build_config_dirs_list once for both policy and cert.
## ${openpgp_policy_file_resolved} / ${openpgp_cert_file_resolved}
## = last existing-and-readable file of each kind, or empty if
## none. Search-paths arrays are kept for sign-and-tag's bail-out
## error message. sign-and-tag bails when the policy is empty;
## signing-key-create uses the cert-resolved value (or writes to
## openpgp_cert_file_user when empty).
if [ -z "${openpgp_policy_file_resolved:-}" ]; then
   openpgp_policy_file_resolved=""
   openpgp_cert_file_resolved=""
   openpgp_policy_search_paths=()
   openpgp_cert_search_paths=()
   for openpgp_policy_dir in $dist_build_config_dirs_list; do
      openpgp_policy_candidate="${openpgp_policy_dir}/${openpgp_policy_filename}"
      openpgp_cert_candidate="${openpgp_policy_dir}/${openpgp_cert_filename}"
      openpgp_policy_search_paths+=( "  ${openpgp_policy_candidate}" )
      openpgp_cert_search_paths+=( "  ${openpgp_cert_candidate}" )
      #printf '%s\n' "openpgp_policy_file_resolved: '$openpgp_policy_file_resolved'"
      if [ -r "${openpgp_policy_candidate}" ]; then
         openpgp_policy_file_resolved="${openpgp_policy_candidate}"
      fi
      #printf '%s\n' "openpgp_cert_candidate: '$openpgp_cert_candidate'"
      if [ -r "${openpgp_cert_candidate}" ]; then
         openpgp_cert_file_resolved="${openpgp_cert_candidate}"
      fi
   done
   unset openpgp_policy_dir openpgp_policy_candidate openpgp_cert_candidate
   ## openpgp_policy_file_resolved="" - in case:
   # > openpgp_cert_candidate: '/home/user/derivative-maker/buildconfig.d/openpgp-cert.pem'
   # > openpgp_cert_candidate: '/etc/buildconfig-dist.d/openpgp-cert.pem'
   # > openpgp_cert_candidate: '/home/user/buildconfig.d/openpgp-cert.pem'
fi

## ${sq_git_policy_file} precedence:
##   1. caller pre-set (env / parse-cmd)
##   2. ${openpgp_policy_file_resolved}
##   3. ${openpgp_policy_file_project}
##
## Redistributable builds must not pick up a contaminated override;
## dm-build-official ensures no openpgp-policy.toml exists in any
## dist_build_config_dirs_list entry above the shipped one, or
## pins sq_git_policy_file explicitly.
[[ -v sq_git_policy_file ]] || \
   sq_git_policy_file="${openpgp_policy_file_resolved:-${openpgp_policy_file_project}}"
export sq_git_policy_file

## }}}

## Late validation: redistributable builds disallow disabling the binary
## derivative repository by default, because the resulting images would not
## be able to fetch updates from the official APT repository. To override
## (e.g. for testing), set 'dist_build_redistributable_allow_no_repo=true'
## via the environment or a buildconfig.d/*.conf file. We don't expose this
## as a command-line flag because it should be a deliberate, persistent
## opt-in, not something pasted into an ad-hoc invocation.
if [ "${dist_build_redistributable:-}" = "true" ] \
   && [ "${build_remote_repo_enable:-}" = "false" ] \
   && [ ! "${dist_build_redistributable_allow_no_repo:-}" = "true" ]; then
   error "\
${red}${bold}'--repo false' (or build_remote_repo_enable=false) is incompatible with a
redistributable build (dist_build_redistributable=true)!

Redistributable images must point at the official APT repository so end users
can receive updates. To override this guard for testing or special-purpose
builds set dist_build_redistributable_allow_no_repo=true via the environment or
a buildconfig.d/*.conf file.${reset}"
fi

## }}}

export make_use_lintian
export genmkfile_make_cmd
export make_use_cowbuilder

[ -n "${signify_folder:-}" ] || signify_folder="$HOMEVAR/.signify"
[ -n "${signify_private_key:-}" ] || signify_private_key="$signify_folder/keyname.sec"
[ -n "${signify_public_key:-}" ] || signify_public_key="$signify_folder/keyname.pub"

## {{{ fallback variables for 'debchange' ('dch'), 'dpkg-buildpackage' and 'debuild'

## see: 'man debchange'
[ -n "${DEBFULLNAME:-}" ] || DEBFULLNAME="derivative distribution auto generated local APT signing key"
export DEBFULLNAME

[ -n "${DEBEMAIL:-}" ] || DEBEMAIL="derivative-distribution@local-signing.key"
export DEBEMAIL

[ -n "${debsign_wrapper:-}" ] || debsign_wrapper="$dist_source_help_steps_folder/sq-debsign-wrapper"
[ -n "${sq_git_wrapper:-}" ] || sq_git_wrapper="$dist_source_help_steps_folder/sq-git-wrapper"
[ -n "${git_verify_command_wrapper:-}" ] || git_verify_command_wrapper=( git -c "gpg.openpgp.program=$sq_git_wrapper" )

## Per-call 'git -c key=value' options that configure sq-git-wrapper
## signing without writing to ~/.gitconfig. user.signingkey is set
## to DEBEMAIL; sq-git-wrapper's case statement detects '@' and
## dispatches to 'sq sign --signer-email'.
git_signing_command_wrapper=(
   git
   -c "user.signingkey=${DEBEMAIL}"
   -c gpg.format=openpgp
   -c "gpg.openpgp.program=${sq_git_wrapper}"
   -c commit.gpgsign=true
   -c tag.gpgsign=true
   -c "user.email=${DEBEMAIL}"
   -c "user.name=${DEBFULLNAME}"
)

[ -n "${make_debsign_opts:-}" ] || make_debsign_opts="-p $debsign_wrapper"
export make_debsign_opts

## For whatever it may be worth.
## https://forums.whonix.org/t/end-to-end-signed-debs-debsign-debsig-and-dpkg-sig
[ -n "${make_use_debsign:-}" ] || make_use_debsign="true"
export make_use_debsign

## }}}

## {{{ apt repository variables

[ -n "${dist_build_apt_codename:-}" ] || dist_build_apt_codename="local"
export dist_build_apt_codename
## developer local setting in buildconfig.d folder / dist_build_source_config_dir function:
## export dist_build_apt_codename="trixie-developers"

## }}}

## {{{ reprepro variables

set_dist_build_reprepro_folder_options() {
   ## derivative_repository_name gets set by packages/kicksecure/genmkfile/usr/share/genmkfile/make-helper-one.bsh
   local derivative_name_item valid_repository_name
   valid_repository_name=false

   if [ "${derivative_repository_name:-}" = "" ]; then
      error "derivative_repository_name is empty!"
   fi
   true "derivative_repository_name: $derivative_repository_name"

   for derivative_name_item in $derivative_name_list ; do
      if [ "${derivative_repository_name:-}" = "$derivative_name_item" ]; then
         valid_repository_name=true
         break
      fi
   done

   if [ ! "${valid_repository_name:-}" = "true" ]; then
      error "derivative_repository_name '$derivative_repository_name' is invalid! (not in derivative_name_list)"
   fi

   [[ -v make_reprepro_codename_item ]] || make_reprepro_codename_item="$dist_build_apt_codename"
   true "make_reprepro_codename_item: $make_reprepro_codename_item"

   if [ ! "${make_reprepro_codename_item:-}" = "local" ]; then
      apt_repo_sub_folder="aptrepo_remote/$derivative_repository_name"
   else
      apt_repo_sub_folder="aptrepo_local/$derivative_repository_name"
   fi

   dist_apt_repository_folder="$binary_build_folder_dist/$apt_repo_sub_folder"
   export dist_apt_repository_folder

   ## Use an array so path components containing whitespace are preserved
   ## correctly. Callers must expand as "${dist_build_reprepro_folder_options[@]}".
   ## Note: bash cannot export arrays. The previous 'export' of a whitespace
   ## joined scalar was a no-op for all practical purposes; no caller was
   ## consuming it anyway.
   dist_build_reprepro_folder_options=(
      --basedir "${source_code_folder_dist}/$apt_repo_sub_folder"
      --outdir  "$binary_build_folder_dist/$apt_repo_sub_folder"
      --dbdir   "$binary_build_folder_dist/$apt_repo_sub_folder/db"
      --logdir  "$binary_build_folder_dist/$apt_repo_sub_folder/log"
   )
}

## }}}

[ -n "${host_architecture:-}" ] || host_architecture="$(dpkg --print-architecture)"
export host_architecture

[ -n "${make_cross_build_platform_list:-}" ] || make_cross_build_platform_list="$dist_build_target_arch"
#[ -n "${make_cross_build_platform_list:-}" ] || make_cross_build_platform_list="i386 amd64"
export make_cross_build_platform_list

## Parent folder holding the cowbuilder 'base.cow_<arch>' / 'cow.cow_<arch>'
## chroots. Overridable so the (multi-gigabyte) chroots can be relocated off a
## small or volatile root filesystem (e.g. onto persistent '/rw' storage on
## Qubes). Exported so child build tools (genmkfile) inherit it.
[ -v cowbuilder_cache_dir ] || cowbuilder_cache_dir="/var/cache/pbuilder"
export cowbuilder_cache_dir

set_cowbuilder_folders() {
   cow_folder="$cowbuilder_cache_dir/cow.cow_${dist_build_multiarch_package_item}"
   base_folder="$cowbuilder_cache_dir/base.cow_${dist_build_multiarch_package_item}"
}

[ -n "${dist_build_file_system:-}" ] || dist_build_file_system="ext4"

## cowbuilder (which internally uses pbuilder).
dist_build_pbuilder_main_chroot_script="$dist_source_help_steps_folder/pbuilder-chroot-script-main"
dist_build_pbuilder_virtualbox_chroot_script="$dist_source_help_steps_folder/pbuilder-chroot-script-virtualbox"

dist_build_pbuilder_config_file="$binary_build_folder_dist/pbuilder.conf"
## export so it can be read by genmkfile
export dist_build_pbuilder_config_file

make_cowbuilder_dist_folder="$binary_build_folder_dist/genmkfile-packages-result"
export make_cowbuilder_dist_folder

$SUDO_TO_ROOT rm --force -- "$dist_build_pbuilder_config_file"
mkdir --parents -- "$binary_build_folder_dist"
mkdir --parents -- "$make_cowbuilder_dist_folder"

printf "%s\n" "\
## This is an auto generated file.
## Auto generated by: '$dist_source_help_steps_folder/$(basename -- "${BASH_SOURCE[0]}")'
## Edits will be lost!

## build-step cowbuilder-setup copies this file into the cowbuilder chroot to:
## '/home/\$user_name/pbuilder_config_file'
## NOTE: The in-chroot path is hardcoded (not derived from \$HOMEVAR) because
## 'pbuilder-chroot-script-main' creates the in-chroot user via 'adduser'
## with '--home /home/\$user_name' regardless of the host's HOMEVAR setting.

## dist_build_pbuilder_config_file is passed to cowbuilder. example:
## '--configfile /home/user/derivative-binary/pbuilder.conf'
## NOTE: Cowbuilder ignores arbitrary environment variables such as 'DIST_APTGETOPT_SERIALIZED'.
##       Therefore, cowbuilder chroot scripts must run:
##       'source /home/$user_name/pbuilder_config_file'
##       Maybe false. Just requires correct 'export'.

## Debugging.
#set -x

## making pbuilder /usr/lib/pbuilder/pbuilder-createbuildenv work with mmdebstrap.
## so it can be read by help-steps/mmdebstrap wrapper
export BUILDPLACE

APTCACHEHARDLINK=no
export APTCACHEHARDLINK

## E: Cross building is possible only with the APT dependency resolver
## Not compatible with 'set -o nounset'.
PBUILDERSATISFYDEPENDSCMD=/usr/lib/pbuilder/pbuilder-satisfydepends-apt
export PBUILDERSATISFYDEPENDSCMD

## End of: '$dist_build_pbuilder_config_file'
" | \
   tee -- "$dist_build_pbuilder_config_file" >/dev/null

#realpath -- "$dist_build_pbuilder_config_file"
#cat -- "$dist_build_pbuilder_config_file"

## Under non-Qubes, resolv.conf is bind-mounted from the host and must not be
## changed for tb-updater to be able to download Tor Browser successfully.
##
## Under Qubes, resolv.conf is provided as part of the template and is
## overridden by Whonix's network config, so resolv.conf.whonix-orig needs to
## be moved back to resolv.conf temporarily to be able to download Tor Browser
## successfully.
##
## Therefore, set tb_disable_anon_ws_dns_conf to false so that
## derivative-maker Whonix builds can download Tor Browser. Qubes builds will
## have this variable set to true, and will therefore also be able to download
## Tor Browser.
tb_disable_anon_ws_dns_conf=false
export tb_disable_anon_ws_dns_conf

## {{{ SKIP_SCRIPTS

## Would be nicer to have this in the 'pre' script, but the 'variables' script
## runs after 'pre' to provide pretty color functions.
##
## NOTE: '$0' refers to the outer build-step script that sourced 'pre'
## (which in turn sources this file), not to 'variables' itself. Matching
## '$0' against SKIP_SCRIPTS is intentional: it lets the caller skip an
## entire build step via e.g. 'SKIP_SCRIPTS+=" 70_log_build_version "'.
##
## NOTE: 'exit 0' (not 'return 0') is intentional and correct. This file is
## sourced from the outer build-step script via 'pre', so 'exit 0' here
## terminates that build step cleanly, which is exactly what skipping
## means. 'return 0' would only stop sourcing 'variables' and let the rest
## of the skipped script continue to run.
own_filename="$(basename -- "$0")"
for skip_script in $SKIP_SCRIPTS; do
   ## '--fixed-strings' so SKIP_SCRIPTS entries are treated as literal
   ## substrings, not regular expressions. Otherwise a '.' or other regex
   ## metacharacter in SKIP_SCRIPTS could match unintended scripts.
   if matched_word=$(printf "%s\n" "$own_filename" | grep --fixed-strings -- "$skip_script") ; then
      printf "%s\n" "${bold}${green}${BASH_SOURCE[0]} INFO: Skipping $own_filename, because SKIP_SCRIPTS matches it. matched_word: '$matched_word'${reset}"
      exit 0
   fi
done
unset skip_script matched_word

## }}}

true "INFO: LD_PRELOAD: ${LD_PRELOAD:-}"

env_vars_keep_list="
   DIST_APTGETOPT_SERIALIZED
   tbb_version
   tb_onion
   tpo_downloader_debug
   tb_disable_anon_ws_dns_conf
   anon_shared_inst_tb
   CURL_PROXY
   CURL_RESUME
   SKIP_SCRIPTS
   SOURCE_DATE_EPOCH
   dist_aptgetopt_file
   dist_build_sources_list_primary
   dist_mmdebstrap_build_sources_list_primary
   dist_build_sources_list_primary_contents
   dist_build_apt_sources_mirror
   dist_build_apt_stable_release
   dist_build_target_arch
   dist_grml_mount_point
   dist_source_help_steps_folder
   dist_build_multiarch_package_item
   dist_build_unsafe_io
   dist_build_version
   derivative_maker
   user_name
   LD_PRELOAD
   LC_ALL
   TZ
   DEBDEBUG
   XZ_OPT
   apt_unattended_opts
   DERIVATIVE_APT_REPOSITORY_OPTS
   DEBOOTSTRAP
   http_proxy
   https_proxy
   REPO_PROXY
   ALL_PROXY
   DEBIAN_FRONTEND DEBIAN_PRIORITY DEBCONF_NOWARNINGS APT_LISTCHANGES_FRONTEND
   INITRD
   HOMEVAR
   HOMEVAR_VBOX_TEMP
   SUDO_TO_ROOT
   SUDO_TO_VBOX_TEMP
   build_dry_run
   dist_build_flavor
   dist_build_type_long
   dist_build_type_short
   dist_build_desktop
   dist_build_virtualbox
   dist_build_qcow2
   dist_build_raw
   dist_build_iso
   dist_build_utm
   dist_build_install_to_root
   dist_build_source_run
   dist_build_fast1
   dist_build_redistributable
   VMNAME
   SHORT_VMNAME
   VMRAM
   VRAM
   dist_unified_name
   vm_names_to_be_exported
   target_architecture_pretty_name
   binary_build_folder_dist
   dist_binary_build_folder
   binary_image_raw_file
   binary_image_raw_file_for_unified
   binary_image_qcow2_file
   binary_image_qcow2_file_for_unified
   binary_image_ova_file
   binary_image_vdi_dir
   cowbuilder_cache_dir
   source_code_folder_dist
   dist_developer_meta_files_folder
   HELPER_SCRIPTS_PATH
"
## TODO:
## UWT_DEV_PASSTHROUGH

true "INFO: Environment variables that will be passed..."

## Re-entrance guard: this block appends to 'sudo_preserve_env_list' and
## 'SUDO_TO_ROOT'. If 'variables' is sourced twice in the same shell (e.g.
## from nested build-step scripts), without this guard '--preserve-env=...'
## would be appended to SUDO_TO_ROOT once per sourcing, producing
## duplicated flags and an ever-growing SUDO_TO_ROOT string.
if [ ! "${variables_sudo_to_root_finalized:-}" = "true" ]; then
   sudo_preserve_env_list=""
   for env_vars_var_keep_item in $env_vars_keep_list ; do
      if [ -z "${env_vars_var_keep_item:-}" ]; then
        error "env_vars_var_keep_item $env_vars_var_keep_item is empty!"
      fi
      if [ "${sudo_preserve_env_list:-}" = "" ]; then
         sudo_preserve_env_list="$env_vars_var_keep_item"
      else
         sudo_preserve_env_list+=",$env_vars_var_keep_item"
      fi
      if [ "$env_vars_var_keep_item" = "DIST_APTGETOPT_SERIALIZED" ]; then
         found=true
      fi
   done

   if ! [ "${found:-}" = "true" ]; then
      error "env_vars_var_keep_item variable DIST_APTGETOPT_SERIALIZED not found!"
   fi
   unset found
   if [ -z "$env_vars_var_keep_item" ]; then
      error "env_vars_var_keep_item variable DIST_APTGETOPT_SERIALIZED is empty!"
   fi

   SUDO_TO_ROOT+=" "
   SUDO_TO_ROOT+="--preserve-env=$sudo_preserve_env_list"

   variables_sudo_to_root_finalized="true"
   export variables_sudo_to_root_finalized
fi

## XXX: Not possible because some scripts use extra sudo arguments such as "--preserve-env".
#SUDO_TO_ROOT+=" "
#SUDO_TO_ROOT+="--"

#printf "%s\n" "INFO: SUDO_TO_ROOT:"
#printf "%s\n" "${cyan}$SUDO_TO_ROOT${reset}"

## Development.
#set -x
#declare -p | awk '{print $3}' | sort | sponge -- ~/temp/varnames
#exit

xtrace_restore

true "${BASH_SOURCE[0]} INFO: End of script, ok."
