#!/bin/bash

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

## The dependency probes in sanity_tests() use 'command -v' rather than 'has'
## (this tool runs early and does not source helper-scripts' has.sh).
## style-ok: no-has

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

true "INFO: Currently running script: ${BASH_SOURCE[0]} $*"

MYDIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" && pwd )"

if [ "$MYDIR" = "/usr/bin" ]; then
   true "INFO: Run from: /usr/bin"
   ## XXX: hardcoded path
   derivative_maker_source_code_dir="$HOME/derivative-maker"
else
   true "INFO: Run from: source code folder"
   derivative_maker_source_code_dir="$(cd -- "$MYDIR" && cd -- "../../../../../" && pwd)"
fi

source "$derivative_maker_source_code_dir/help-steps/pre"
source "$derivative_maker_source_code_dir/help-steps/variables"

cd -- "${dist_binary_build_folder}"

sanity_tests() {
   command -v -- mktorrent >/dev/null
   command -v -- transmission-show >/dev/null
   command -v -- sq >/dev/null
   command -v -- signify-openbsd >/dev/null
   test -f "${signify_public_key}"
   test -f "${signify_private_key}"
   test -f "${binary_image_installer_dist_source}"

   "${dist_source_help_steps_folder}/signing-key-test" "$@"
}

installer_dist() {
   if [ ! "${dist_build_installer_dist:-}" = "true" ]; then
      return 0
   fi

   command -v -- git >/dev/null
   command -v -- str_replace >/dev/null

   cp -- "${binary_image_installer_dist_source}" "${binary_image_installer_dist_file}"

   installer_dist_source_folder="$(dirname -- "${binary_image_installer_dist_source}")"
   pushd -- "${installer_dist_source_folder}"
   git_tag_current="$(git describe --always --abbrev=1000000000)"
   ## example git_tag_current:
   ## 11.7-1
   ## example git_tag_current:
   ## 11.7-1-23-g5e70e4d81e494e66b296f89136b1ac332e8a738d
   popd

   search="commit-hash-replace-me"
   replace="${git_tag_current}"
   file_name="${binary_image_installer_dist_file}"
   str_replace "${search}" "${replace}" "${file_name}"
}

windows_installer() {
   if [ ! "${dist_build_windows_installer:-}" = "true" ]; then
      true "$0: INFO: Skip ${FUNCNAME[0]}, because dist_build_windows_installer is not set to true."
      return 0
   fi

   if ! test -r "${binary_image_ova_file}" ; then
      error "$0: binary_image_ova_file ${binary_image_ova_file} does not exist!"
   fi

   test -d "$binary_build_folder_dist/virtualbox-windows-installer-binary"
   pushd -- "$binary_build_folder_dist/virtualbox-windows-installer-binary" >/dev/null

   local counter
   counter=0
   local selected_file_name=""
   for file_name in ./VirtualBox-*.exe ; do
      ## Default glob behaviour: with no matches the pattern is left
      ## literal, so guard against that explicitly. Also guard against
      ## accidentally matched directory names.
      test -f "${file_name}" || continue
      selected_file_name="${file_name}"
      counter=$((counter + 1))
   done

   if [ "${counter}" -eq "0" ]; then
      true "$0: ERROR: No file matching VirtualBox-*.exe!"
      exit 1
   fi
   if [ "${counter}" -gt "1" ]; then
      true "$0: ERROR: Multiple files matching VirtualBox-*.exe!"
      exit 1
   fi

   test -r "${selected_file_name}"
   FILE_VBOX_INST_EXE=$(realpath "${selected_file_name}")
   test -r "${FILE_VBOX_INST_EXE}"

   "${dist_developer_meta_files_folder}/usr/bin/dm-virtualbox-installer-exe-verify-windows"

   FILE_VCREDIST_INST_EXE=$(realpath "vc_redist.x64.exe")
   test -r "$FILE_VCREDIST_INST_EXE"

   "${dist_developer_meta_files_folder}/usr/bin/dm-virtualbox-installer-vc_redist-verify-windows"

   popd >/dev/null

   ## environment variables read by Whonix-Installer build.sh
   TARGET_SYSTEM="WINDOWS"
   FILE_LICENSE="${source_code_folder_dist}/packages/whonix/whonix-base-files/usr/share/whonix/WHONIX_BINARY_LICENSE_AGREEMENT"
   FILE_WHONIX_OVA="${binary_image_ova_file}"
   FILE_WHONIX_STARTER_MSI="${source_code_folder_dist}/windows/Whonix-Starter/WhonixStarterInstaller.msi"
   #FILE_VBOX_INST_EXE="${FILE_VBOX_INST_EXE}"
   FILE_INSTALLER_BINARY_FINAL="${binary_image_windows_installer_file}"
   export TARGET_SYSTEM FILE_LICENSE FILE_VCREDIST_INST_EXE FILE_WHONIX_OVA FILE_WHONIX_STARTER_MSI FILE_VBOX_INST_EXE FILE_INSTALLER_BINARY_FINAL

   VERSION="${dist_build_version}"
   VERSION_FULL=$(basename -- "${binary_image_ova_file}")
   MANUFACTURE="ENCRYPTED SUPPORT LLC"
   DESCRIPTION="Whonix-Starter"
   export VERSION VERSION_FULL MANUFACTURE DESCRIPTION

   pushd -- "${source_code_folder_dist}/windows/Whonix-Starter" >/dev/null
   ## Make sure everything is rebuilt and no previously auto generated files are accidentally reused.
   git clean --force -d -X
   ## Creates WhonixStarterInstaller.msi
   ./build_on_linux_for_win64
   ## Not possible at this time as this would delete WhonixStarterInstaller.msi
   #git clean --force -d -X
   popd >/dev/null

   safe-rm --force -- "${binary_image_windows_installer_file}"

   pushd -- "${source_code_folder_dist}/windows/Whonix-Installer" >/dev/null
   git clean --force -d -X
   ## Requires already existing WhonixStarterInstaller.msi
   ## Creates ${binary_image_windows_installer_file}
   "${source_code_folder_dist}/windows/Whonix-Installer/build.sh"
   git clean --force -d -X
   popd >/dev/null

   pushd -- "${source_code_folder_dist}/windows/Whonix-Starter" >/dev/null
   ## Delete WhonixStarterInstaller.msi and other auto generated files.
   git clean --force -d -X
   popd >/dev/null

   test -r "${binary_image_windows_installer_file}"
}

libvirt_compress() {
   local continue_maybe
   continue_maybe=no
   if [ "${dist_build_raw:-}" = "true" ]; then
      true "dist_build_raw: true"
      continue_maybe=yes
   fi
   if [ "${dist_build_qcow2:-}" = "true" ]; then
      true "dist_build_qcow: true"
      continue_maybe=yes
   fi
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      true "dist_build_source_archive: true"
      continue_maybe=yes
   fi
   if [ "${continue_maybe}" = "no" ]; then
      return 0
   fi

   if [ "${vm_multiple}" = "" ]; then
      error "Variable vm_multiple is unset!"
   fi

   legal_files_copy

   if [ "${dist_build_qcow2:-}" = "true" ]; then
      safe-rm --force -- "$(basename -- "${libvirt_target_qcow2_xz_archive_file}")"
      true "INFO: Checking if all files to be added to libvirt_target_qcow2_xz_archive_file ${libvirt_target_qcow2_xz_archive_file} exist..."
   fi
   if [ "${dist_build_raw:-}" = "true" ]; then
      safe-rm --force -- "$(basename -- "${libvirt_target_raw_xz_archive_file}")"
      true "INFO: Checking if all files to be added to libvirt_target_raw_xz_archive_file ${libvirt_target_raw_xz_archive_file} exist..."
   fi
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      safe-rm --force -- "$(basename -- "${source_archive_xz_file}")"
      true "INFO: Checking if all files to be added to source_archive_xz_file ${source_archive_xz_file} exist..."
   fi

   true "dist_build_type_short: $dist_build_type_short"
   if [ "${dist_build_type_short}" = "kicksecure" ]; then
      test -f "${dist_binary_build_folder}/KICKSECURE_BINARY_LICENSE_AGREEMENT"
      test -f "${dist_binary_build_folder}/KICKSECURE_DISCLAIMER"
   elif [ "${dist_build_type_short}" = "whonix" ]; then
      test -f "${dist_binary_build_folder}/WHONIX_BINARY_LICENSE_AGREEMENT"
      test -f "${dist_binary_build_folder}/WHONIX_DISCLAIMER"
   fi
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      true "dist_build_source_archive..."
   fi

   true "vm_multiple: $vm_multiple"
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      true "dist_build_source_archive..."
   elif [ "${vm_multiple}" = "false" ]; then
      true "vm_multiple: no"
      true "SHORT_VMNAME: $SHORT_VMNAME"
      test -f "${dist_binary_build_folder}/${SHORT_VMNAME}.xml"
      if [ "${dist_build_raw:-}" = "true" ]; then
         test -f "${binary_image_raw_file}"
      fi
      if [ "${dist_build_qcow2:-}" = "true" ]; then
         test -f "${binary_image_qcow2_file}"
      fi
   else
      true "vm_multiple: yes"
      test -f "${dist_binary_build_folder}/Whonix-Gateway.xml"
      test -f "${dist_binary_build_folder}/Whonix-Workstation.xml"

      ## The Whonix-External / Whonix-Internal libvirt network XML files were
      ## removed from the 'libvirt-dist' package, so 'build-steps.d/*_export-
      ## libvirt-xml' no longer generates them and they are not part of the
      ## release archive anymore. Left commented (not deleted) to document the
      ## former behavior in case per-network XML returns.
      #true "libvirt_target_network_file_external: $libvirt_target_network_file_external"
      #test -f "${libvirt_target_network_file_external}"
      #true "libvirt_target_network_file_internal: $libvirt_target_network_file_internal"
      #test -f "${libvirt_target_network_file_internal}"

      if [ "${dist_build_raw:-}" = "true" ]; then
         test -f "${binary_image_raw_file_for_unified}"
         test -f "${binary_image_raw_file}"
         true "INFO: Ok, all files to be added to ${libvirt_target_raw_xz_archive_file} exist."
      fi
      if [ "${dist_build_qcow2:-}" = "true" ]; then
         test -f "${binary_image_qcow2_file_for_unified}"
         test -f "${binary_image_qcow2_file}"
         true "INFO: Ok, all files to be added to ${libvirt_target_qcow2_xz_archive_file} exist."
      fi
   fi

   ## Not using `gzip`, because it cannot handle sparse files:
   ## https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=535987
   #pv "${dist_binary_build_folder}/Whonix-Gateway-${dist_build_version}.qcow2" | gzip -n - > "${dist_binary_build_folder}/Whonix-Gateway-${dist_build_version}.libvirt.tar.gz"
   #pv "${dist_binary_build_folder}/Whonix-Workstation-${dist_build_version}.qcow2" | gzip -n - > "${dist_binary_build_folder}/Whonix-Workstation-${dist_build_version}.libvirt.tar.gz"

   ## Using `tar` with `--xz` and `--mtime="2014-05-06 00:00:00"`:
   ## - will result in a deterministic archive. We do not have deterministic
   ##   images yet in the first place (but at least verifiable ones), but
   ##   hopefully some day. Does not hurt to already use tar options that do
   ##   not add unnecessary non-deterministic contents.
   ## Not using tar --verbose:
   ## - slows down tar too much.

   local -a file_list_compress_full_path
   file_list_compress_full_path=()

   if [ "${dist_build_source_archive:-}" = "true" ]; then
      file_list_compress_full_path+=("${source_code_folder_dist}")
   elif [ "${dist_build_type_short}" = "kicksecure" ]; then
      file_list_compress_full_path+=("${dist_binary_build_folder}/KICKSECURE_BINARY_LICENSE_AGREEMENT")
      file_list_compress_full_path+=("${dist_binary_build_folder}/KICKSECURE_DISCLAIMER")
   elif [ "${dist_build_type_short}" = "whonix" ]; then
      file_list_compress_full_path+=("${dist_binary_build_folder}/WHONIX_BINARY_LICENSE_AGREEMENT")
      file_list_compress_full_path+=("${dist_binary_build_folder}/WHONIX_DISCLAIMER")
   fi

   true "vm_multiple: $vm_multiple"
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      true "dist_build_source_archive..."
   elif [ "${vm_multiple}" = "false" ]; then
      true "vm_multiple: false"
      file_list_compress_full_path+=("${dist_binary_build_folder}/${SHORT_VMNAME}.xml")

      if [ "${dist_build_raw:-}" = "true" ]; then
         file_list_compress_full_path+=("${binary_image_raw_file}")
      fi
      if [ "${dist_build_qcow2:-}" = "true" ]; then
         file_list_compress_full_path+=("${binary_image_qcow2_file}")
      fi
   else
      true "vm_multiple: true"
      file_list_compress_full_path+=("${dist_binary_build_folder}/Whonix-Gateway.xml")
      file_list_compress_full_path+=("${dist_binary_build_folder}/Whonix-Workstation.xml")
      ## Whonix-External / Whonix-Internal network XML removed from
      ## 'libvirt-dist'; no longer generated, so not archived. See the
      ## matching commented-out 'test -f' block above.
      #file_list_compress_full_path+=("${libvirt_target_network_file_external}")
      #file_list_compress_full_path+=("${libvirt_target_network_file_internal}")

      true "dist_build_raw for raw image: ${dist_build_raw:-}"
      if [ "${dist_build_raw:-}" = "true" ]; then
         file_list_compress_full_path+=("${binary_image_raw_file_for_unified}")
         file_list_compress_full_path+=("${binary_image_raw_file}")
      fi
      true "dist_build_qcow2 for qcow image: ${dist_build_qcow2:-}"
      if [ "${dist_build_qcow2:-}" = "true" ]; then
         file_list_compress_full_path+=("${binary_image_qcow2_file_for_unified}")
         file_list_compress_full_path+=("${binary_image_qcow2_file}")
      fi
   fi

   ## XXX: --target raw and --target qcow2 can currently not be combined but that seems unlikely.
   ## XXX: --target source can currently not be combined.
   true "dist_build_source_archive: ${dist_build_source_archive:-}"
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      xz_archive="${source_archive_xz_file}"
   fi
   true "dist_build_raw: ${dist_build_raw:-}"
   if [ "${dist_build_raw:-}" = "true" ]; then
      xz_archive="${libvirt_target_raw_xz_archive_file}"
   fi
   true "dist_build_qcow2: ${dist_build_qcow2:-}"
   if [ "${dist_build_qcow2:-}" = "true" ]; then
      xz_archive="${libvirt_target_qcow2_xz_archive_file}"
   fi

   true "file_list_compress_full_path: ${file_list_compress_full_path[*]}"

   local test_operator
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      test_operator="-d"
      $SUDO_TO_ROOT safe-rm --force --recursive -- "${dist_binary_build_folder}/derivative-maker"
      cp --archive --no-dereference --preserve=all -- "${source_code_folder_dist}" "${dist_binary_build_folder}"
   else
      test_operator="-f"
   fi

   local -a filelist_base_name
   filelist_base_name=()

   local file_name_item_full_path
   local base_name_file_item_full_path
   for file_name_item_full_path in "${file_list_compress_full_path[@]}" ; do
      test "${test_operator}" "${file_name_item_full_path}"
      base_name_file_item_full_path="$(basename -- "${file_name_item_full_path}")"
      filelist_base_name+=("$base_name_file_item_full_path")
   done

   local file_name_item_base_name
   for file_name_item_base_name in "${filelist_base_name[@]}" ; do
      test "${test_operator}" "${file_name_item_base_name}"
   done

   ## TODO: add progress meter (using `pv`?)
   tar \
      --create \
      --owner=0 --group=0 --numeric-owner \
      --mode=go=rX,u+rw,a-s \
      --sort=name \
      --sparse \
      --mtime='2015-10-21 00:00Z' \
      --xz \
      --directory="${dist_binary_build_folder}" \
      --file "$(basename -- "${xz_archive}")" \
      "${filelist_base_name[@]}"

   strip-nondeterminism "${xz_archive}"

   sync

   ## sanity test
   if [ ! -r "${xz_archive}" ]; then
      error "xz_archive ${xz_archive} not readable!"
   fi

   if [ "${dist_build_source_archive:-}" = "true" ]; then
      ## Not deleting source code.
      return 0
   fi

   for file_name_item_full_path in "${file_list_compress_full_path[@]}" ; do
      ## Safe disk space.
      ## Delete raw and ova images in ${dist_binary_build_folder} after xz archive has been created.
      safe-rm --force -- "${file_name_item_full_path}"
   done
}

virtualbox_export() {
   if [ ! "${dist_build_virtualbox:-}" = "true" ]; then
      return 0
   fi

   if [ "${vm_names_to_be_exported}" = "" ]; then
      error "Variable vm_names_to_be_exported is unset!"
   fi
   if [ "${vm_multiple}" = "" ]; then
      error "Variable vm_multiple is unset!"
   fi

   legal_files_copy

   local producturl vendorurl vendor eulafile_user description
   if [ "${dist_build_type_short}" = "kicksecure" ]; then
      producturl="https://www.${project_clearnet}"
      vendor="Kicksecure"
      eulafile_user="${dist_binary_build_folder}/KICKSECURE_BINARY_LICENSE_AGREEMENT"
   else
      producturl="https://www.${project_clearnet}/wiki/Other_Operating_Systems"
      vendor="Whonix"
      eulafile_user="${dist_binary_build_folder}/WHONIX_BINARY_LICENSE_AGREEMENT"
   fi

   vendorurl="https://www.${project_clearnet}"
   description="\
Build Version [A]: ${dist_build_version}

Stay tuned! [B]

Please donate! [C]

[A] https://www.${project_clearnet}/wiki/Build_Version
[B] https://www.${project_clearnet}/wiki/Stay_Tuned
[C] https://www.${project_clearnet}/wiki/Donate"

   ## VirtualBox is installed and the VM is registered inside the amd64
   ## cowbuilder chroot (see 'build-steps.d/*_create-vbox-vm'), not on the
   ## build host. So run the 'VBoxManage export' / 'import --dry-run' portion
   ## inside that chroot via 'cowbuilder --execute', mirroring
   ## 'build-steps.d/*_create-vbox-vm'. Signing and upload stay on the host
   ## (where the operator's credentials already are); only the VBoxManage
   ## portion runs in the chroot. The '.ova' is written to
   ## '$binary_image_ova_file' under the bind-mounted
   ## '$binary_build_folder_dist', so it lands on the host.
   local dist_build_multiarch_package_item
   dist_build_multiarch_package_item="amd64"
   export dist_build_multiarch_package_item

   ## Sets base_folder / cow_folder for the current
   ## $dist_build_multiarch_package_item.
   set_cowbuilder_folders

   if [ ! -d "$base_folder" ]; then
      error "base_folder '$base_folder' does not exist. Did the amd64 cowbuilder chroot (build-steps.d/*_create-vbox-vm) fail to build?"
   fi

   ## Hand the chroot script its variables through the environment via the
   ## existing '--preserve-env' below, rather than serialising them to a file
   ## the chroot sources. Export each set entry of the curated allowlist
   ## '$env_vars_keep_list' (several are in the list but not exported, so the
   ## '--preserve-env=' on '$SUDO_TO_ROOT' would otherwise skip them) plus the
   ## export-specific presentation strings computed above. Re-using the
   ## allowlist means there is no parallel hardcoded list to drift.
   local env_vars_var_keep_item export_env_var_item
   for env_vars_var_keep_item in $env_vars_keep_list; do
      ## Skip variables unset in this run; an unset allowlist entry is not an
      ## error.
      [ -v "$env_vars_var_keep_item" ] || continue
      # shellcheck disable=SC2163  # dynamic export of the allowlisted name is intended
      export "$env_vars_var_keep_item"
   done
   for export_env_var_item in producturl vendor vendorurl description eulafile_user vm_multiple project_clearnet; do
      # shellcheck disable=SC2163
      export "$export_env_var_item"
   done

   local chroot_exec_script
   chroot_exec_script="$dist_source_help_steps_folder/pbuilder-chroot-script-export-vbox-vm"

   local bindmounts
   ## Keep host and chroot absolute paths identical so $binary_image_ova_file
   ## and the bind-mounted legal files resolve correctly inside the chroot.
   ## Space-separated by design: pbuilder consumes BINDMOUNTS with an unquoted
   ## word-split ('for mnt in $BINDMOUNTS' in pbuilder-modules' mount loop), so a
   ## bind path containing whitespace is unsupported by pbuilder itself. Neither a
   ## bash array nor repeated '--bindmounts' flags help: each flag just appends to
   ## the same whitespace-split BINDMOUNTS string. These are controlled build
   ## paths with no spaces (help-steps/variables fails early otherwise).
   ## '$binary_image_vdi_dir' (the VM disk work folder created by
   ## 'build-steps.d/*_create-vbox-vm') is bind-mounted as its OWN mount so the
   ## unprivileged chroot account 'dm-vbox-temp' can traverse to the disk that
   ## the VirtualBox registry references by absolute path; see
   ## 'binary_image_vdi_dir' in 'help-steps/variables'. The bind source must
   ## exist before cowbuilder mounts it.
   mkdir --parents -- "$binary_image_vdi_dir"
   bindmounts="$binary_build_folder_dist $source_code_folder_dist $binary_image_vdi_dir"

   local cowbuilder_exit_code
   cowbuilder_exit_code=0

   ## '--preserve-env' carries the variables exported above through cowbuilder
   ## into the chroot script's environment; no env file is passed.
   $SUDO_TO_ROOT \
      --preserve-env \
         ${COWBUILDER_PREFIX:-} \
            cowbuilder \
               --host-arch "$host_architecture" \
               --architecture "$dist_build_multiarch_package_item" \
               --configfile "$dist_build_pbuilder_config_file" \
               --execute \
               --basepath "$base_folder" \
               --buildplace "$cow_folder" \
               --save-after-login \
               --bindmounts "$bindmounts" \
               -- \
               "$chroot_exec_script" \
               || { cowbuilder_exit_code="$?" ; true; };

   if [ -d "$base_folder" ]; then
      $SUDO_TO_ROOT "$dist_source_help_steps_folder/umount_kill.sh" "$base_folder"
   fi

   if [ ! "${cowbuilder_exit_code:-}" = "0" ]; then
      error "cowbuilder --execute pbuilder-chroot-script-export-vbox-vm failed with exit code '$cowbuilder_exit_code'."
   fi

   ## The chroot wrote '$binary_image_ova_file' (root-owned, world-readable)
   ## onto the bind-mount. Restore normal-user ownership on the host.
   $SUDO_TO_ROOT chown -- "$user_name:$user_name" "${binary_image_ova_file}"

   ## Sanity test.
   test -r "${binary_image_ova_file}"

   sync
}

sign_cmd_openpgp() {
   local base_name
   base_name="$(basename -- "$1")"
   test -f "$1"
   test -n "${DEBEMAIL:-}"
   safe-rm -f -- "$2"
   ## OpenPGP signatures do not authenticate filenames by default, therefore add
   ## the name of the file as a OpenPGP notation so at least users that look
   ## at OpenPGP notations have a chance to detect if file names have been
   ## tampered with.
   sq sign --signature-notation "file@name" "$base_name" --signer-email "$DEBEMAIL" --signature-file="$2" -- "$1"
   test -f "$2"
}

sign_cmd_signify() {
   ## To create a key for signify-openbsd, see:
   ## derivative-maker help-steps/signing-key-create

   ## Big file are unfortunately unsupported by signify.
   if [ "$file_size_mb" -ge "1000" ]; then
      return 0
   fi
   ## https://forums.whonix.org/t/signify-openbsd/7842
   signify-openbsd -S -s "${signify_private_key}" -m "$1" -x "$2"
   ## Sanity test.
   test -f "$2"
}

verify_cmd_openpgp() {
   test -f "$1"
   test -f "$2"
   test -n "${DEBEMAIL:-}"
   sq verify --signer-email "$DEBEMAIL" --signature-file="$2" -- "$1"
}

verify_cmd_signinfy() {
   ## Match the threshold in sign_cmd_signify; otherwise files in the
   ## 100..999 MB band would be signed but never re-verified, and any
   ## bug in signing would go undetected.
   if [ "$file_size_mb" -ge "1000" ]; then
      return 0
   fi
   signify-openbsd -V -p "${signify_public_key}" -m "$1" -x "$2"
}

sign_and_verify() {
   file_size_bytes=$(stat -c%s "$1")
   # Convert bytes to megabytes
   file_size_mb=$(printf '%s\n' "scale=0; $file_size_bytes / 1024 / 1024" | bc)

   sign_cmd_openpgp "${1}" "${1}.asc"
   verify_cmd_openpgp "${1}" "${1}.asc"
   sign_cmd_signify "${1}" "${1}.sig"
   verify_cmd_signinfy "${1}" "${1}.sig"
}

signatures_create() {
   local var_names file_list upload_url_part
   var_names=()
   file_list=()
   upload_url_part=()

   if [ "${dist_build_virtualbox:-}" = "true" ]; then
      var_names+=("binary_image_ova_file")
      file_list+=("${binary_image_ova_file}")
      upload_url_part+=("ova")
   fi
   if [ "${dist_build_raw:-}" = "true" ]; then
      var_names+=("libvirt_target_raw_xz_archive_file")
      file_list+=("${libvirt_target_raw_xz_archive_file}")
      upload_url_part+=("raw")
   fi
   if [ "${dist_build_qcow2:-}" = "true" ]; then
      var_names+=("libvirt_target_qcow2_xz_archive_file")
      file_list+=("${libvirt_target_qcow2_xz_archive_file}")
      upload_url_part+=("libvirt")
   fi
   if [ "${dist_build_iso:-}" = "true" ]; then
      var_names+=("binary_image_iso_file")
      file_list+=("${binary_image_iso_file}")
      upload_url_part+=("iso")
   fi
   if [ "${dist_build_installer_dist:-}" = "true" ]; then
      var_names+=("binary_image_installer_dist_file")
      file_list+=("${binary_image_installer_dist_file}")
      upload_url_part+=("dist-installer-cli")
   fi
   if [ "${dist_build_windows_installer:-}" = "true" ]; then
      var_names+=("binary_image_windows_installer_file")
      file_list+=("${binary_image_windows_installer_file}")
      upload_url_part+=("windows")
   fi
   if [ "${dist_build_source_archive:-}" = "true" ]; then
      var_names+=("source_archive_xz_file")
      file_list+=("${source_archive_xz_file}")
      upload_url_part+=("source")
   fi

   ## Thanks to:
   ## https://github.com/moba/createtortorrents/blob/master/createtorrents.sh
   ##
   ## List of trackers (Use with care!):
   ## https://github.com/XIU2/TrackersListCollection
   TRACKERS=""
   ## https://fosstorrents.com/tracker/
   TRACKERS+="http://fosstorrents.com:6969/announce"
   TRACKERS+=","
   TRACKERS+="udp://fosstorrents.com:6969/announce"
   TRACKERS+=","
   ## https://opentrackr.org/
   TRACKERS+="udp://tracker.opentrackr.org:1337/announce"
   TRACKERS+=","
   ## https://torrent.eu.org/
   TRACKERS+="udp://tracker.torrent.eu.org:451"
   true "TRACKERS: ${TRACKERS}"

   ## Debugging.
   pwd

   local i
   for i in "${!file_list[@]}"; do
      true "Variable Name: ${var_names[i]}"
      true "Variable Content: ${file_list[i]}"
      true "upload_url_part: ${upload_url_part[i]}"

      local file_item url_part_item
      file_item="${file_list[i]}"
      url_part_item="${upload_url_part[i]}"

      local base_name
      base_name=$(basename -- "${file_item}")

      safe-rm --force -- "${file_item}.torrent"
      mktorrent \
         --verbose \
         --announce="${TRACKERS}" \
         --web-seed "https://download.${project_clearnet}/${url_part_item}/${dist_build_version}/${base_name}" \
         -o "${file_item}.torrent" \
         "${file_item}"
      transmission-show "${file_item}.torrent"

      ## Using `basename` so we end up with relative paths in hash sums file.
      sha512sum -- "${base_name}" | tee -- "${file_item}.sha512sums" >/dev/null
      sha512sum --check -- "${file_item}.sha512sums"

      sign_and_verify "${file_item}"
      sign_and_verify "${file_item}.torrent"
      sign_and_verify "${file_item}.sha512sums"
   done
}

legal_files_copy() {
   ## libvirt_compress uses --directory="${dist_binary_build_folder}"
   if [ "${dist_build_type_short}" = "kicksecure" ]; then
      cp -- "${source_code_folder_dist}/packages/kicksecure/kicksecure-base-files/usr/share/kicksecure/KICKSECURE_BINARY_LICENSE_AGREEMENT" "${dist_binary_build_folder}/KICKSECURE_BINARY_LICENSE_AGREEMENT"
      cp -- "${source_code_folder_dist}/packages/kicksecure/kicksecure-base-files/usr/share/kicksecure/KICKSECURE_DISCLAIMER" "${dist_binary_build_folder}/KICKSECURE_DISCLAIMER"
   else
      cp -- "${source_code_folder_dist}/packages/whonix/whonix-base-files/usr/share/whonix/WHONIX_BINARY_LICENSE_AGREEMENT" "${dist_binary_build_folder}/WHONIX_BINARY_LICENSE_AGREEMENT"
      cp -- "${source_code_folder_dist}/packages/whonix/whonix-base-files/usr/share/whonix/WHONIX_DISCLAIMER" "${dist_binary_build_folder}/WHONIX_DISCLAIMER"
   fi
}

main() {
   if [ "${dist_build_utm:-}" = "true" ]; then
      true "dist_build_utm: true"
      true "INFO: Skipping ${FUNCNAME[0]}, because not implemented for UTM."
      exit 0
   fi

   sanity_tests "$@"

   if [ "${dist_build_type_long}" = "gateway" ]; then
      true "$0: INFO: Skip, because dist_build_type_long is gateway. Not needed because of unified Whonix-Gateway and Whonix-Workstation images."
      return 0
   fi

   installer_dist
   libvirt_compress
   virtualbox_export
   windows_installer
   signatures_create
   sync
}

main "$@"
