#!/bin/bash

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

## general example usage:

## 1)
#make_cross_build_platform_list="i386 amd64 arm64" ./build-steps.d/*_cowbuilder-setup --allow-untagged true --allow-uncommitted true --flavor internal --target root

## 2)
#make_cross_build_platform_list="i386 amd64 arm64" ./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor internal --target root

## specific example usage:

#make_cross_build_platform_list="i386 amd64 arm64" ./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor internal --target root --function download_tpo_packages

#./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor internal --target virtualbox --function download_packages_from_debian_sid

## TODO: always check packages/kicksecure/anon-shared-build-apt-sources-tpo/etc/apt/sources.list.d/torproject.sources first!

set -x
set -e

true "INFO: Currently running script: $BASH_SOURCE $@"

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

cd "$MYDIR"
cd ..
cd help-steps

dist_build_internal_run="true"

source pre
source colors
source variables

cd "$MYDIR"
cd ..

download_tpo_packages() {
   ## https://forums.whonix.org/t/tor-integration-in-whonix/10593/2
   available_architectures_for_download_only_list="i386 amd64 arm64"
   dist_build_special_packages_chroot_script="$dist_source_help_steps_folder/repo_download_chroot_script"
   repo_signing_key="$source_code_folder_dist/packages/kicksecure/anon-shared-build-apt-sources-tpo/usr/share/anon-shared-build-apt-sources-tpo/tpoarchive-keys.d/torprojectarchive.asc"
   repo_sources_list="$dist_build_sources_list_torproject"
   temp_newer_packages="$binary_build_folder_dist/temp_packages_tpo"
   export apt_target_release="none"
   export download_source_package="true"

   ## Selected packages we want to mirror from deb.torproject.org to Kicksecure local/remote repository.
   newer_package_list="tor tor-geoipdb deb.torproject.org-keyring"

   build_run_function get_newer_packages_from_third_party_repositories "$@"
}

download_virtualbox_packages_virtualbox_org() {
   ## Only when using '--target virtualbox'.
   if [ ! "$dist_build_virtualbox" = "true" ]; then
      true "${green}INFO: Skipping ${under}$FUNCNAME${eunder}, because dist_build_virtualbox is not set to 'true'.${reset}"
      return 0
   fi

   error "Function download_virtualbox_packages_virtualbox_org is currently not needed and incomplete."
   return 0

   ## XXX: Better set to "i386 amd64" which are the only actually available platforms for this repo_sources_list?
   ## Or better set available_architectures_for_download_only_list="all" to make the build process fail early?
   ## But does not matter since this code is only processed when using '--target virtualbox'.
   ## Only platforms i386 amd64 are available from this repo_sources_list.
   ## (Even packages.debian.org having amd64 only https://packages.debian.org/sid/virtualbox at time of writing.)
   ## Creating '--target virtualbox' on platforms other than amd64 such as arm64 would fail anyhow since
   ## nobody is providing packages for these platforms. At time of writing, nobody reported trying that either.
   available_architectures_for_download_only_list="i386 amd64"
   dist_build_special_packages_chroot_script="$dist_source_help_steps_folder/repo_download_chroot_script"
   repo_signing_key="$source_code_folder_dist/build_sources/oracle_vbox_2016.asc"
   ## TODO: repo_sources_list - low priority - currently not needed
   repo_sources_list=""
   temp_newer_packages="$binary_build_folder_dist/temp_packages_virtualbox_org"
   export apt_target_release="none"
   ## Not available from that repository but from Debian directly.
   export download_source_package="false"

   ## Selected packages we want to mirror from repo_sources_list to Whonix local/remote repository.
   newer_package_list="virtualbox-7.2"

   build_run_function get_newer_packages_from_third_party_repositories "$@"
}

download_packages_from_debian_sid() {
   ## Using 'sid' instead of 'unstable'.
   ## github.com/grml/grml-debootstrap/issues/37

   available_architectures_for_download_only_list="all"
   dist_build_special_packages_chroot_script="$dist_source_help_steps_folder/repo_download_chroot_script"
   repo_signing_key="none"
   repo_sources_list="$dist_build_sources_list_debian_sid"
   temp_newer_packages="$binary_build_folder_dist/temp_packages_debian_sid"
   export apt_target_release="sid"
   export download_source_package="true"

   newer_package_list=""

   #if [ ! "$dist_build_iso" = "true" ]; then
      #return 0
   #fi
   ## On Debian trixie, installing calamares from sid has lead to (dependency or other?) issues
   ## A proper backport would be required.
   #if [ "$dist_build_iso" = "true" ]; then
      #newer_package_list="calamares"
   #fi

   if [ "$dist_build_virtualbox" = "true" ] || [ "$dist_build_iso" = "true" ]; then
      ## See:
      ## https://forums.whonix.org/t/challenges-installing-virtualbox/9984/7
      ##
      ## comment outdated:
      ##
      ## Upstream virtualbox.org does not provide guest additions debian packages:
      ## (Neither VirtualBox guest addition ISO nor VirtualBox guest addition debian package.)
      ## https://www.virtualbox.org/ticket/734
      ##
      ## https://packages.debian.org/bullseye/virtualbox-guest-additions-iso
      ## provides:
      ## /usr/share/virtualbox/VBoxGuestAdditions.iso
      ##
      ## nonfreedom vs freedom discussion:
      ## https://www.whonix.org/wiki/Dev/VirtualBox#VirtualBox_Unavailable_in_Debian_main_due_to_Licensing_Issues
      ## https://forums.virtualbox.org/viewtopic.php?f=10&t=21374&p=477656#p477656
      ## https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=962311
      ## https://www.virtualbox.org/ticket/19751

      ## Selected packages we want to mirror from repo_sources_list to derivative local/remote repository.

      ## quote debian/changelog
      ## * Drop virtualbox guest modules dkms and sources, as in Ubuntu and Debian,
      ## all kernel flavours provide those from the upstream kernel since at least
      ## focal 20.04 LTS.
      ## Thus these are not needed anymore. LP: #1933248
      ##
      ## please drop virtualbox-guest-dkms virtualbox-guest-source
      ## https://bugs.launchpad.net/ubuntu/+source/virtualbox/+bug/1933248

      newer_package_list="virtualbox-guest-additions-iso"

      if [ "$dist_build_target_arch" = "amd64" ] || [ "$dist_build_target_arch" = "i386" ]; then
         newer_package_list+=" virtualbox-guest-utils "
         newer_package_list+=" virtualbox-guest-x11 "

         #if [ "$dist_build_virtualbox" = "true" ]; then
            ## These packages are not needed for the ISO.
            ##
            ## These 'unstable' ('sid') packages are longer compatible with 'trixie'.
            #newer_package_list+=" virtualbox "
            #newer_package_list+=" virtualbox-dkms "
            #newer_package_list+=" virtualbox-qt "
            #newer_package_list+=" virtualbox-source "
         #fi
      fi
   fi

   if [ -n "${newer_package_list}" ]; then
      build_run_function get_newer_packages_from_third_party_repositories "$@"
   fi
}

## usage example for manual testing:
#./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor internal --target iso --function download_packages_from_debian_forky_backports
download_packages_from_debian_forky_backports() {
   available_architectures_for_download_only_list="all"
   dist_build_special_packages_chroot_script="$dist_source_help_steps_folder/repo_download_chroot_script"
   repo_signing_key="none"
   repo_sources_list="$dist_build_sources_list_debian_forky_backports"
   temp_newer_packages="$binary_build_folder_dist/temp_packages_debian_forky_backports"
   export apt_target_release="forky-backports"
   export download_source_package="false"

   newer_package_list=""

   ## No need for calamares from debian.backports in Trixie at the moment.
   #if [ "$dist_build_iso" = "true" ]; then
   #   true "INFO: Download package from backports for ISO build..."
   #   newer_package_list="calamares"
   #else
   #   true "INFO: No packages from backports required for non-ISO builds at this time, skipping, ok."
   #   return 0
   #fi

   true "INFO: No packages from backports required for non-ISO builds at this time, skipping, ok."
   return 0

   build_run_function get_newer_packages_from_third_party_repositories "$@"
}

get_newer_packages_from_third_party_repositories() {
   true "${cyan}$BASH_SOURCE INFO: Downloading newer packages from third-party repository... ${reset}"

   local cow_folder base_folder

   if [ "$temp_newer_packages" = "" ]; then
      error "variable temp_newer_packages is unset!"
      exit 1
   fi

   $SUDO_TO_ROOT safe-rm --recursive --force -- "$temp_newer_packages"
   mkdir --parents -- "$temp_newer_packages"

   for dist_build_multiarch_package_item in $make_cross_build_platform_list ; do
      export dist_build_multiarch_package_item

      architecture_available=""
      for available_architectures_for_download_only_item in $available_architectures_for_download_only_list ; do
         if [ "all" = "$available_architectures_for_download_only_item" ]; then
            true "${cyan}INFO: available_architectures_for_download_only_item: '$available_architectures_for_download_only_item'${reset}"
            architecture_available="true"
            continue
         fi
         if [ "$dist_build_multiarch_package_item" = "$available_architectures_for_download_only_item" ]; then
            true "${cyan}INFO: available_architectures_for_download_only_item: '$available_architectures_for_download_only_item'${reset}"
            architecture_available="true"
            continue
         fi
      done
      if [ ! "$architecture_available" = "true" ]; then
         true "${red}${bold}INFO: Skipping,
download newer_package_list: ${under}$newer_package_list${eunder}
for platform: ${under}$dist_build_multiarch_package_item${eunder}
because dist_build_multiarch_package_item: $dist_build_multiarch_package_item
is not included in available_architectures_for_download_only_list: $available_architectures_for_download_only_list${reset}"
         return 0
      fi

      if [ "$make_use_cowbuilder" = "true" ]; then
         ## Implemented in help-steps/variables.
         ## sets:
         ## cow_folder
         ## base_folder
         set_cowbuilder_folders

         $SUDO_TO_ROOT test -d "${base_folder}"
         $SUDO_TO_ROOT "$dist_source_help_steps_folder/umount_kill.sh" "${base_folder}"
      else
         base_folder="$binary_build_folder_dist/no-pbuilder/base"
         ## Not needed. For completeness sake. Just in case.
         cow_folder="$binary_build_folder_dist/no-pbuilder/cow"

         if_no_pbuilder_base_folder="${base_folder}"
         export if_no_pbuilder_base_folder
      fi

      $SUDO_TO_ROOT mkdir --parents -- "${base_folder}/var/cache/derivative-maker/temporary-repository"

      if [ "$repo_signing_key" = "none" ]; then
         $SUDO_TO_ROOT safe-rm -f -- "${base_folder}/var/cache/derivative-maker/temporary-repository/repo_signing_key.asc"
      else
         $SUDO_TO_ROOT cp -- "$repo_signing_key" "${base_folder}/var/cache/derivative-maker/temporary-repository/repo_signing_key.asc"
      fi

      if [ "$repo_sources_list" = "none" ]; then
         $SUDO_TO_ROOT safe-rm -f -- "${base_folder}/var/cache/derivative-maker/temporary-repository/newer.sources"
      else
         $SUDO_TO_ROOT cp -- "$repo_sources_list" "${base_folder}/var/cache/derivative-maker/temporary-repository/newer.sources"
      fi

      ## TOOD: Not required?
      #$SUDO_TO_ROOT cp -- "$dist_build_pbuilder_config_file" "${base_folder}/var/cache/derivative-maker/temporary-repository"

      local benchmark_time_start benchmark_took_time
      benchmark_time_start="$(date +%s)" || true
      local dist_build_download_script_exit_code
      dist_build_download_script_exit_code=0

      if [ "$make_use_cowbuilder" = "true" ]; then
         $SUDO_TO_ROOT \
            --preserve-env \
               $COWBUILDER_PREFIX \
               newer_package_list="$newer_package_list" \
               temp_newer_packages="$temp_newer_packages" \
                  cowbuilder \
                     --host-arch "$host_architecture" \
                     --architecture "$dist_build_target_arch" \
                     --configfile "$dist_build_pbuilder_config_file" \
                     --execute "$dist_build_special_packages_chroot_script" \
                     --basepath "${base_folder}" \
                     --buildplace "$cow_folder" \
                     --bindmounts "$temp_newer_packages" \
                     || { dist_build_download_script_exit_code="$?" ; true; };

            if [ -d "${base_folder}" ]; then
               $SUDO_TO_ROOT "$dist_source_help_steps_folder/umount_kill.sh" "${base_folder}"
            fi
      else
         export newer_package_list
         export temp_newer_packages
         $SUDO_TO_ROOT "$dist_build_special_packages_chroot_script" || { dist_build_download_script_exit_code="$?" ; true; };

         ## XXX
         ## help-steps/repo_download_chroot_script messes up APT downloaded sources
         $SUDO_TO_ROOT apt-get "${APTGETOPT[@]}" update
      fi

      $SUDO_TO_ROOT chown --recursive "$user_name:$user_name" "$temp_newer_packages"

      benchmark_took_time="$(benchmarktimeend "$benchmark_time_start")" || true
      true "${cyan}INFO: Done with function ${under}$FUNCNAME${eunder} (benchmark: $benchmark_took_time) ${reset}"

      if [ ! "$dist_build_download_script_exit_code" = "0" ]; then
         return "$dist_build_download_script_exit_code"
      fi
   done

   true "${cyan}$BASH_SOURCE INFO: Completed downloading newer packages from third-party repository. ${reset}"

   reprepro_add_newer_packages_from_third_party_repositories
}

reprepro_add_newer_packages_from_third_party_repositories() {
   true "${cyan}$BASH_SOURCE INFO: Adding newer packages from third-party repository to reprepro... ${reset}"
   if [ "$temp_newer_packages" = "" ]; then
      error "Variable temp_newer_packages is empty!"
   fi

   ## read by dm-reprepro-wrapper
   derivative_repository_name="kicksecure"
   export derivative_repository_name

   local file_name package_absolute_path file_name_only

   for file_name in "$temp_newer_packages/"*".deb" ; do
      package_absolute_path="$(readlink -f "$file_name")"
      file_name_only="${package_absolute_path##*/}"
      package=${file_name_only%%_*}

      "$dist_developer_meta_files_folder/usr/bin/dm-reprepro-wrapper" remove "$dist_build_apt_codename" "$package" || true
      "$dist_developer_meta_files_folder/usr/bin/dm-reprepro-wrapper" removesrc "$dist_build_apt_codename" "$package" || true
   done

   for file_name in "$temp_newer_packages/"*".dsc" ; do
      package_absolute_path="$(readlink -f "$file_name")"
      file_name_only="${package_absolute_path##*/}"
      package=${file_name_only%%_*}

      "$dist_developer_meta_files_folder/usr/bin/dm-reprepro-wrapper" remove "$dist_build_apt_codename" "$package" || true
      "$dist_developer_meta_files_folder/usr/bin/dm-reprepro-wrapper" removesrc "$dist_build_apt_codename" "$package" || true
   done

   for file_name in "$temp_newer_packages/"*".deb" ; do
      package_absolute_path="$(readlink -f "$file_name")"
      "$dist_developer_meta_files_folder/usr/bin/dm-reprepro-wrapper" includedeb "$dist_build_apt_codename" "$package_absolute_path"
   done

   for file_name in "$temp_newer_packages/"*".dsc" ; do
      package_absolute_path="$(readlink -f "$file_name")"
      "$dist_developer_meta_files_folder/usr/bin/dm-reprepro-wrapper" includedsc "$dist_build_apt_codename" "$package_absolute_path"
   done

   true "${cyan}$BASH_SOURCE INFO: Added newer packages from third-party repository to reprepro. ${reset}"
}

create_derivative_distribution_debian_packages() {
   true "${cyan}$BASH_SOURCE INFO: Creating Derivative Debian Packages... ${reset}"

   ## Sanity test.
   printf '%s\n' "" | wc -l >/dev/null

   local item number_folders_total number_folder_current benchmark_time_start benchmark_took_time
   ## Thanks to: l0b0 - http://unix.stackexchange.com/a/20855/49297
   number_folders_total="$(find "$source_code_folder_dist/packages" -mindepth 2 -maxdepth 2 -type d -printf '\n' | wc -l)" || true
   number_folder_current="0"

   shopt -s nullglob
   shopt -s dotglob

   local derivative_name_item
   for derivative_name_item in $derivative_name_list ; do
      for item in $pkg_list ; do
         item="$source_code_folder_dist/packages/$derivative_name_item/$item"
         if [ ! -d "$item" ]; then
            true "INFO: Not a folder: $item"
            continue
         fi

         pushd "$item" >/dev/null
         number_folder_current="$(( number_folder_current + 1 ))"
         local base_name
         base_name="${item##*/}"

#          if [ ! "$base_name" = "hardened-kernel" ]; then
#             true "${cyan}INFO: Skipping $base_name as expected.${reset}"
#             popd
#             continue
#          fi

         ## 'Architecture:' currently in ./packages/$derivative_name_item/ folder:
         ## all
         ## any
         ## amd64
         ## linux-amd64
         if cat "debian/control" | grep --invert-match "\#" | grep "Architecture:" | grep "all" ; then
            true "DEBUG: Architecture: all"
         elif cat "debian/control" | grep --invert-match "\#" | grep "Architecture:" | grep "any" ; then
            true "DEBUG: Architecture: any"
         elif cat "debian/control" | grep --invert-match "\#" | grep "Architecture:" | grep "amd64" ; then
            true "DEBUG: Architecture: amd64"
            if [ ! "$dist_build_target_arch" = "amd64" ]; then
               true "${cyan}INFO: Skipping platform 'amd64' specific package $base_name as expected.${reset}"
               popd >/dev/null
               continue
            fi
         elif cat "debian/control" | grep --invert-match "\#" | grep "Architecture:" | grep "linux-amd64" ; then
            true "DEBUG: Architecture: linux-amd64"
            if [ ! "$dist_build_target_arch" = "amd64" ]; then
               true "${cyan}INFO: Skipping platform 'linux-amd64' specific package $base_name as expected.${reset}"
               popd >/dev/null
               continue
            fi
         else
            cat "debian/control" | grep "Architecture:" || true
            error "unknown Architecture:"
         fi

         true "${cyan}INFO: Will build now package ($number_folder_current / $number_folders_total): $base_name ${reset}"
         benchmark_time_start="$(date +%s)" || true
         lintian_use_maybe="$make_use_lintian"
         "$source_code_folder_dist/packages/kicksecure/genmkfile/usr/bin/genmkfile" reprepro-remove
         "$source_code_folder_dist/packages/kicksecure/genmkfile/usr/bin/genmkfile" deb-cleanup
         if [ "$make_use_cowbuilder" = "false" ]; then
            "$source_code_folder_dist/packages/kicksecure/genmkfile/usr/bin/genmkfile" deb-build-dep
         fi
         ## Environment variables make_use_lintian, make_use_cowbuilder, dist_build_apt_codename if set are expected to be passed.
         "$source_code_folder_dist/packages/kicksecure/genmkfile/usr/bin/genmkfile" deb-pkg

         "$source_code_folder_dist/packages/kicksecure/genmkfile/usr/bin/genmkfile" reprepro-add
         benchmark_took_time="$(benchmarktimeend "$benchmark_time_start")" || true
         popd >/dev/null
         true "${cyan}INFO: Done with build of package ($number_folder_current / $number_folders_total) (benchmark: $benchmark_took_time): $base_name ${reset}"
      done
   done

   true "${cyan}$BASH_SOURCE INFO: Created Derivative Debian Packages. ${reset}"
}

create-debian-packages() {
   if [ "$dist_build_apt_codename" = "" ]; then
      error "Variable dist_build_apt_codename is empty!"
   fi
   true "${cyan}$BASH_SOURCE INFO: Going to update ${under}${dist_build_apt_codename}${eunder} APT repository... ${reset}"
   true "make_cross_build_platform_list: ${under}$make_cross_build_platform_list${eunder}"
   #sleep 3

   if [ ! "$FUNCTION" = "" ]; then
      $FUNCTION
      return 0
   fi

   #build_run_function download_virtualbox_packages_virtualbox_org "$@"
   build_run_function download_packages_from_debian_sid "$@"
   build_run_function download_packages_from_debian_forky_backports "$@"

   ## https://www.whonix.org/wiki/Dev/Tor
   ## https://forums.whonix.org/t/tor-integration-in-whonix/10593/57
   build_run_function download_tpo_packages "$@"

   build_run_function create_derivative_distribution_debian_packages "$@"
}

main() {
   if [ "$build_dry_run" = "true" ]; then
      true "${bold}${cyan}INFO: dry-run, skipping $BASH_SOURCE. ${reset}"
      return 0
   fi

   if [ "$dist_build_flavor" = "whonix-custom-workstation" ]; then
      true "${cyan}INFO: Skipping creation of packages for custom workstation.${reset}"
   elif [ "$build_remote_derivative_pkgs" = "true" ]; then
      true "${cyan}INFO: build_remote_derivative_pkgs is set to $build_remote_derivative_pkgs, skipping creation of packages.${reset}"
   else
      build_run_function create-debian-packages "$@"
   fi
}

build_run_function main "$@"
