#!/bin/bash

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

# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/check_runtime.bsh
source /usr/libexec/helper-scripts/check_runtime.bsh

: "${tpo_downloader_debug:=""}"
if [ ! "$tpo_downloader_debug" = "" ] && [[ "$tpo_downloader_debug" =~ ^[0-9]+$ ]]; then
   if [ "$tpo_downloader_debug" -ge "1" ]; then
      set -x
   fi
fi

if was_executed "${BASH_SOURCE[0]}"; then
  ## Script was executed.
  tb_updater_was_sourced="false"
else
  ## Script was sourced.
  ## This is useful for other programs / scripts to be able to `source` the
  ## functions of this script for code reuse. dist-installer-gui will do this.
  tb_updater_was_sourced="true"
fi

if [ "$tb_updater_was_sourced" = "false" ]; then
  set -o errexit
  # shellcheck disable=SC2317
  set -o pipefail
  set -o errtrace
fi

who_ami="$(whoami)"

# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/strings.bsh
source /usr/libexec/helper-scripts/strings.bsh

## sets: boot_session
# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/boot-session-detection.bsh
if test -f /usr/libexec/helper-scripts/boot-session-detection.sh; then
   ## Legacy.
   source /usr/libexec/helper-scripts/boot-session-detection.sh
else
   ## Newer.
   source /usr/libexec/helper-scripts/boot-session-detection.bsh
fi

# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/has.sh
source /usr/libexec/helper-scripts/has.sh
# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/package_installed_check.sh
source /usr/libexec/helper-scripts/package_installed_check.sh
# shellcheck source=../../../gpg-bash-lib/usr/libexec/gpg-bash-lib/source_all
source /usr/libexec/gpg-bash-lib/source_all
## shellcheck source=../../../../helper-scripts/usr/libexec/log_run_die.sh
source /usr/libexec/helper-scripts/log_run_die.sh

log info "Start."

[[ -v SCRIPTNAME ]] || SCRIPTNAME="$(basename "$BASH_SOURCE")"
[[ -v ICON ]] || ICON="/usr/share/icons/icon-pack-dist/tbupdate.ico"

## Developer comment on this script:
## I would be very happy if this script would not be required.
## Unfortunately, it is required. It works around a lot bugs:
## - There is no deb/rpm/whatever package with Tor Browser.
##   https://gitlab.torproject.org/tpo/community/trac/-/issues/5236
##   https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/3994
## - There is no way to add the Documentation page well visible to Tor Browser:
##   https://gitlab.torproject.org/legacy/trac/-/issues/6025
##   https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/6053
## - Tor Browsers support for Isolating and Transparent Proxies is very limited:
##   https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/5611
## - RecommendedTBBVersions format is not finalized
##   https://gitlab.torproject.org/tpo/web/tpo/-/issues/307
## - counter downgrade / stale mirror attacks on RecommendedTBBVersions - sign / verify tbb versions file
##   https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/13065
## - GETINFO consensus/packages exists but not yet populated.
##   https://gitlab.torproject.org/tpo/core/tor/-/issues/10395
## - Using torbrowser-launcher instead of tb-updater in Whonix
##   https://forums.whonix.org/t/using-torbrowser-launcher-instead-of-tb-updater-in-whonix/385

tb_exit_function() {
   local exit_code="$1"
   trap "" ERR

   if [ "$exit_code" = "0" ]; then
      log notice "END: Success."
      exit 0
   fi

   if [ "$anon_shared_inst_tb" = "open" ]; then
      if [ "$tb_postinst" = "true" ]; then
         log notice "END: Failing open.
More info:
$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Tor_Browser_Update:_Technical_Details"
      fi
      exit 0
   else
      if [ "$tb_postinst" = "true" ]; then
         log error "END: Failing closed.
More info:
$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Tor_Browser_Update:_Technical_Details"
      fi
      exit "$exit_code"
   fi
}

tb_error_handler() {
   local exit_code="$?"
   trap "" ERR

   local MSG="<p>###########################################################
<br></br>## $SCRIPTNAME script bug.
<br></br>## No panic. Nothing is broken. Just some rare condition
<br></br>## has been hit. Try again later. There is likely a
<br></br>## solution for this problem. Please see the News,
<br></br>## User Help Forum and Documentation.
<br></br>## <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki>$tb_documentation_base_url_clearnet/wiki/$tb_wiki</a>
<br></br>## Please report this bug!
<br></br>##
<br></br>## who_ami: <code>$who_ami</code>
<br></br>## BASH_COMMAND: <code>$BASH_COMMAND</code>
<br></br>## exit_code: <code>$exit_code</code>
<br></br>##
<br></br>## output_opts: <code>${output_opts[*]}</code>
<br></br>## progressbaridx: <code>$progressbaridx</code>
<br></br>##
<br></br>## For debugging, run:
<br></br>## <code>$SCRIPTNAME</code> --debug
<br></br>###########################################################</p>"

   [ -n "$tb_user_home" ] || tb_user_home=~
   [ -n "$tb_install_folder" ] || tb_install_folder="tb"

   if test -w "$tb_user_home/.cache/$tb_install_folder" ; then
      mkdir --parents -- "$tb_user_home/.cache/$tb_install_folder"
   fi
   if test -w "$tb_user_home/.cache/$tb_install_folder/torbrowser_updater_error.log" ; then
      append "$tb_user_home/.cache/$tb_install_folder/torbrowser_updater_error.log" "$MSG"
   fi

   ## In case function output_gui does not exist yet.
   if [ ! "$(type -t "output_gui")" = "function" ]; then
      output_gui() {
         stecho
      }
   fi

   if [ "$progressbaridx" = "" ]; then
      true
   else
      output_gui ${output_opts[@]} --progressbaridx "$progressbaridx" --progressx "100" || true
      progressbaridx=""
   fi

   output_gui ${output_opts[@]} --messagex --titlex "$TITLE" --typex "error" --message "$MSG" --done
   output_gui ${output_opts[@]} --messagecli --titlecli "$TITLE" --typecli "error" --message "$MSG" --done
   tb_exit_function 1
}

trap "tb_error_handler" ERR

download_fail_help_set() {
   DOWNLOAD_FAIL_HELP="<p>Possible reasons for download failure:</p>
<ul>
  <li>Internet connectivity issue.</li>
  <li>The download server is down.</li>
  <li>File size exceeded (endless data attack triggered).</li>
  <li>Other software update attack (rollback, indefinite freeze).</li>
  <li><code>$tb_title</code> Downloader (by <code>$tb_downloader_developers</code> developers) has been broken due to upstream changes.</li>
</ul>
<p>Recommendations:</p>
<ul>
  <li>Re-run <code>$SCRIPTNAME</code> with or without <code>--resume<c/ode> in terminal. For example: <code>$SCRIPTNAME --resume</code></li>
  <li>For more information, see the <a href='$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Troubleshooting'>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Troubleshooting</a>.</li>
</ul>"
}

tb_run_function() {
   [[ -v "${tb_skip_functions}" ]] || tb_skip_functions=''
   case $tb_skip_functions in
   *"$@"*) log notice "Skipping '$*', because tb_skip_functions includes it."
                  return 0
                  ;;
   esac

   true "INFO: Running '$*', because tb_skip_functions does not include it."
   "$@"
}

root_check() {
   local tb_sandbox_exit_code
   if [ "$(id --user)" != "0" ]; then
     true "INFO: ${FUNCNAME[0]}: running as unprivileged account (non-root)."
     tb_running_as_root=false
     return 0
   fi
   tb_running_as_root=true

   if printf '%s\n' "$@" | grep -- "--postinst" >/dev/null 2>/dev/null ; then
      log notice "Dropping privileges to account 'tb-updater' because using '--postinst' option."
      tb_sandbox_exit_code='0'
      /usr/libexec/tb-updater/sandbox-update-torbrowser "$@" || tb_sandbox_exit_code="$?"
      tb_exit_function "$tb_sandbox_exit_code"
   fi

   log error "Do not run '$SCRIPTNAME' as root (sudo)! Instead, '$SCRIPTNAME' should be run as unprivileged account (non-root)."
   tb_exit_function 2
}

qubes_template_check() {
   local tb_sandbox_exit_code
   if ! [ -f /run/qubes/this-is-templatevm ]; then
      log notice "Not running in a Qubes TemplateVM, ok."
      return 0
   fi
   if [ "${who_ami}" = 'tb-updater' ]; then
      log notice "Running in a Qubes TemplateVM as user 'tb-updater', ok."
      return 0
   fi
   if pkg_installed user-sysmaint-split && [ "$(whoami)" != 'sysmaint' ]; then
      log error "'$SCRIPTNAME' must be run using account 'sysmaint' when running in a Qubes TemplateVM with user-sysmaint-split installed!"
      tb_exit_function 2
   fi
   tb_sandbox_exit_code='0'
   log notice "Dropping privileges to account 'tb-updater' because running in a Qubes TemplateVM, ok."
   sudo /usr/libexec/tb-updater/sandbox-update-torbrowser "$@" || tb_sandbox_exit_code="$?"
   tb_exit_function "$tb_sandbox_exit_code"
}

sysmaint_check() {
   if ! [ "$boot_session" = "sysmaint_session" ]; then
      true "INFO: sysmaint_session: no"
      if [ "$who_ami" != "sysmaint" ]; then
         true "INFO: sysmaint_session: no and not running as sysmaint, ok."
         return 0
      fi
      ## No need to check for account tb-updater, since we're blacklisting
      ## accounts here, not whitelisting them.
      log error "Do not run '$SCRIPTNAME' under account '$who_ami' in user session! Instead, in user session '$SCRIPTNAME' should be run under account 'user' or another unprivileged user."
      tb_exit_function 2
   fi

   true "INFO: sysmaint_session: yes"
   if [ "$who_ami" = "sysmaint" ]; then
      true "INFO: sysmaint_session: yes and running as account 'sysmaint', ok."
      return 0
   fi
   ## We do need to check for account tb-updater here because we're
   ## whitelisting accounts in this scenario.
   if [ "$who_ami" = "tb-updater" ]; then
      true "INFO: sysmaint_session: yes and running as account 'tb-updater', ok."
      return 0
   fi
   log error "Do not run '$SCRIPTNAME' under account '$who_ami' in sysmaint session! Instead, in sysmaint session '$SCRIPTNAME' should be run under account 'sysmaint'."
   tb_exit_function 2
}

tb_sanity_tests() {
   has id
   has basename
   has touch
   has uname
   has mkdir
   has pidof
   has chmod
   has cp
   has mv
   has killall
   has safe-rm
   has head
   has tar
   has date
   has /usr/libexec/msgcollector/msgcollector
   has /usr/libexec/msgcollector/pv_wrapper
   ## tbbversion function
   has grep
   ## tbbversion function
   has sed
   has sort
   has pv
   has mkfifo
   has jq
   has sanitize-string
}

tb_ex_funct() {
   local MSG
   MSG="$0: INFO: ${FUNCNAME[0]}: Signal '$SIGNAL_TYPE' received. Cleaning up..."

   if [ "$last_pid_list" = "" ]; then
      true
   else
      local childpids_list childpid_item
      for pid in $last_pid_list; do
         childpids_list=$(pgrep --parent "$pid")
         for childpid_item in $childpids_list ; do
            pkill --signal sigkill --parent "$childpid_item" &>/dev/null || true
         done
      done
   fi

   if [ "$progressbaridx" = "" ]; then
      true
   else
      output_gui ${output_opts[@]} --progressbaridx "$progressbaridx" --progressx "100" || true
      progressbaridx=""
   fi

   output_gui ${output_opts[@]} --messagecli --typecli "info" --message "$MSG" --done

   #MSG="Aborted."
   #output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done
   #log error "$MSG"

   MSG="$0: INFO: ${FUNCNAME[0]}: Signal '$SIGNAL_TYPE' received. Exiting."
   output_gui ${output_opts[@]} --messagecli --typecli "info" --message "$MSG" --done
}

tb_signal_sigterm() {
   SIGNAL_TYPE="sigterm"
   tb_ex_funct
   tb_exit_function 143
}

trap "tb_signal_sigterm" SIGTERM

tb_signal_sigint() {
   SIGNAL_TYPE="sigint"
   tb_ex_funct
   tb_exit_function 130
}

trap "tb_signal_sigint" SIGINT ## ctrl + c

trap_sigusr2() {
   SIGNAL_TYPE="SIGUSR2"
   tb_ex_funct
   tb_exit_function 3
}

trap "trap_sigusr2" SIGUSR2 ## msgcollector, yad cancel button

tb_parse_cmd_options() {
   ## Thanks to:
   ## https://mywiki.wooledge.org/BashFAQ/035

   while :
   do
       case $1 in
           --debug)
               set -x
               shift
               ;;
           --ordinary)
               ordinary="true"
               shift
               ;;
           --update)
               true ## legacy
               shift
               ;;
           --noask)
               TB_FORCE_INSTALL="1"
               shift
               ;;
           --only-if-newer)
               UPDATE_ONLY_IF_NEWER="1"
               shift
               ;;
           --nokilltb)
               NOKILLTB="1"
               shift
               ;;
           --postinst)
               tb_postinst="true"
               UPDATE_ONLY_IF_NEWER="1"
               shift
               ;;
           --is-chroot)
               is_chroot="true"
               shift
               ;;
           --noaskstart)
               noaskstart="true"
               shift
               ;;
           --alpha)
               tbb_download_alpha_version="true"
               shift
               ;;
           --no-tor-con-check)
               TB_NO_TOR_CON_CHECK="1"
               shift 1
               ;;
           --input)
               TB_INPUT="$2"
               shift 2
               ;;
           --no-install)
               TB_NO_INSTALL="true"
               shift
               ;;
           --resume)
               CURL_RESUME="--continue-at -"
               shift
               ;;
           --onion)
               [ -n "$tb_onion" ] || tb_onion="true"
               shift
               ;;
           --reset)
               [ -n "$tb_hard_reset" ] || tb_hard_reset="true"
               [ -n "$TB_NO_CLEANUP" ] || TB_NO_CLEANUP="true"
               shift
               ;;
           --)
               shift
               break
               ;;
           -*)
               log error "Unknown option: '$1'"
               log error "See:"
               log error "    man $SCRIPTNAME"
               tb_exit_function 4
               ;;
           *)
               break
               ;;
       esac
   done

   ## If there are input files (for example) that follow the options, they
   ## will remain in the "$@" positional parameters.
   true "$BASH_SOURCE \$*: $*"
}

tb_stdin() {
   if [ ! "$TB_INPUT" = "" ]; then
      true "INFO: TB_INPUT is already set to '$TB_INPUT', skipping auto detection, ok."
      return 0
   fi
   if [ "$TB_FORCE_INSTALL" = "1" ]; then
      log notice "TB_FORCE_INSTALL is set to '1', therefore setting TB_INPUT to 'none'."
      TB_INPUT="none"
      export TB_USE_MSGCOLLECTOR="false"
      return 0
   fi
   if [ -t "0" ]; then
      #log notice "stdin connected to terminal, setting \
#TB_INPUT to 'stdin', will use terminal for input, ok."
      #log notice "Alternatively, if want to run from \
#command line, but still use the graphical user interface for input, \
#you could add to command line: --input gui"
      TB_INPUT="stdin"
      export TB_USE_MSGCOLLECTOR="false"
      return 0
   fi
   log notice "stdin not connected to terminal, probably run in graphical environment, ok."
}

tb_qubes_dvm_template() {
   if printf '%s\n' "$qubes_vm_name" | grep --invert-match -- "-dvm" >/dev/null 2>/dev/null ; then
      log notice "Not running inside Qubes Disposable Template, ok."
      return 0
   fi

   local MSG="\
<p>Do not run <code>$TITLE</code> in Qubes Disposable Template!<br></br>
<br></br>
More info: <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Running_Tor_Browser_in_Qubes_Template_or_Disposable_Template>$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Running_Tor_Browser_in_Qubes_Template_or_Disposable_Template</a></p>"

   output_gui ${output_opts[@]} --messagex --titlex "$TITLE" --typex "error" --message "$MSG" --done

   output_cli error "$MSG"

   tb_exit_function 1
}

output_cli() {
   local log_level MSG
   log_level="$1"
   shift || true
   MSG="$(sanitize-string -- nolimit "$@")"
   log "$log_level" "$MSG"
}

output_gui() {
   local MSG output_tool
   true "${FUNCNAME[0]}: START"
   true "${FUNCNAME[0]}: args: ${1+$@}"
   output_tool="/usr/libexec/msgcollector/msgcollector"
   ## Variable initialization required because 'curl-prgrs' uses "set -o nounset".
   [[ -v TB_USE_MSGCOLLECTOR ]] || TB_USE_MSGCOLLECTOR=""
   [[ -v outputfunc_verbose ]] || outputfunc_verbose=""
   if [ "$TB_USE_MSGCOLLECTOR" = "false" ]; then
      while true; do
         case $1 in
            --messagex)
               ## Only show --messagecli. Skip --messagex.
               true "${FUNCNAME[0]}: --messagex END"
               return 0
               ;;
            --message)
               MSG="$2"
               shift 2
               break
               ;;
            *)
               break
               ;;
         esac
      done
      ## MSG will be unset for '--progressx'.
      [[ -v MSG ]] || MSG=""
      if [ ! "$MSG" = "" ]; then
         MSG="$(sanitize-string -- nolimit "$MSG")"
         log notice "$MSG"
      fi
   elif [ "$outputfunc_verbose" = "true" ]; then
      printf '%s\n' "RUNNING: bash -x $output_tool --identifier $IDENTIFIER ${1+$@}"
      bash -x $output_tool --identifier "$IDENTIFIER" "$@"
   else
      $output_tool --identifier "$IDENTIFIER" "$@"
   fi
   true "${FUNCNAME[0]}: END END"
}

tb_config_folder_parser() {
   true "tb_settings_folder: $tb_settings_folder"
   [ -n "$tb_settings_folder" ] || tb_settings_folder="torbrowser.d"
   shopt -s nullglob
   local i
   for i in \
      "/etc/${tb_settings_folder}"/*.conf \
      "/usr/local/etc/${tb_settings_folder}"/*.conf \
      ; do
         bash -n "$i"
         source "$i"
   done
}

tb_settings_chroot_common() {
   ## Do not run in chroot if tb_install_in_chroot=false.
   if [ ! "$tb_install_in_chroot" = "false" ]; then
      [ -n "$tb_updater_run" ] || tb_updater_run=true
   fi
   ## Fail open, if there was an error.
   [ -n "$anon_shared_inst_tb" ] || anon_shared_inst_tb=open
   ## Skip Tor connectivity check when running inside chroot.
   [ -n "$TB_NO_TOR_CON_CHECK" ] || TB_NO_TOR_CON_CHECK="1"
   ## Hack to disable using proxy settings when running inside chroot.
   ## We are using --fail anyhow. No problem to duplicate it.
   [ -n "$CURL_PROXY" ] || CURL_PROXY=( "--fail" )

   ## Careful as it would leak information to logs:
   ## Connection #0 to host 10.137.0.82 left intact
   #[ -n "$CURL_OPTS" ] || CURL_OPTS="--verbose"

   ## Debugging.
   [ -n "$pv_wrapper_debug" ] || pv_wrapper_debug=true
}

tb_settings_postinst_common() {
   ## Skip installation confirmation messages when running with
   ## --postinst, because we will be using hardcoded version numbers to verify
   ## upstream's version info.
   ## Downgrade / freeze attacks should not be possible, because file names
   ## that include the version number will be verified using gpg.
   [ -n "$tb_confirm_installation_skip" ] || tb_confirm_installation_skip=true

   [ -n "$NOKILLTB" ] || NOKILLTB="1"
   [ -n "$noaskstart" ] || noaskstart="true"
   [ -n "$TB_INPUT" ] || TB_INPUT="none"
   export TB_USE_MSGCOLLECTOR="false"

   ## Fail open, if there was an error.
   [ -n "$anon_shared_inst_tb" ] || anon_shared_inst_tb=open

   ## Using curl instead of curl-prgrs to debug server issue.
   ## --ordinary
   #[ -n "$ordinary" ] || ordinary="true"

   ## Careful as it would like information to logs:
   ## Connection #0 to host 10.137.0.82 left intact
   #[ -n "$CURL_OPTS" ] || CURL_OPTS="--verbose"
}

tb_settings_qubes_common_templatevm() {
   ## Skip Tor connectivity check when running inside Qubes Template,
   ## because since Qubes R4 Templates are non-networked by default.
   [ -n "$TB_NO_TOR_CON_CHECK" ] || TB_NO_TOR_CON_CHECK="1"
}

tb_settings_not_templatevm() {
   ## TODO: Unused, remove?
   true
}

tb_settings_qubes_postinst_templatevm() {
   ## Do not run during Qubes Template postinst if tb_install_follow=false.
   if [ ! "$tb_install_follow" = "false" ]; then
      [ -n "$tb_updater_run" ] || tb_updater_run=true
   fi
}

tb_settings_qubes_manual_run_templatevm() {
   [ -n "$tb_updater_run" ] || tb_updater_run=true
}

tb_settings_manual_run_common() {
   [ -n "$tb_updater_run" ] || tb_updater_run=true
}

tb_preparation() {
   [ -n "$tb_wiki" ] || tb_wiki="Tor_Browser"
   [ -n "$tb_title" ] || tb_title="Tor Browser"
   [ -n "$IDENTIFIER" ] || IDENTIFIER="torbrowser-downloader"
   [ -n "$tb_install_folder" ] || tb_install_folder="tb"
   [ -n "$tb_install_folder_dot" ] || tb_install_folder_dot=".tb"
   [ -n "$tb_browser_name" ] || tb_browser_name="tor-browser"
   [ -n "$tb_bin" ] || tb_bin="torbrowser"
   [ -n "$tb_downloader_developers" ] || tb_downloader_developers="Whonix"
   [ -n "$tb_documentation_base_url_clearnet" ] || tb_documentation_base_url_clearnet="https://www.whonix.org"
   [ -n "$tb_global_binary_dir" ] || tb_global_binary_dir='/var/cache/tb-binary'

   if [ "$tb_postinst" = "true" ]; then
      [ -n "$tb_manual_run" ] || tb_manual_run=false
   else
      [ -n "$tb_manual_run" ] || tb_manual_run=true
   fi

   if has qubesdb-read; then
      [ -n "$is_qubes" ] || is_qubes=true

      ## qubesdb-read fails
      ## - inside chroot,
      ## - perhaps during upgrades
      ##   - https://github.com/QubesOS/qubes-issues/issues/2497
      ##   - https://github.com/QubesOS/qubes-issues/issues/2509
      ## therefore overwriting with '|| true'.
      [ -n "$qubes_vm_name" ] || qubes_vm_name="$(qubesdb-read /name)" || true
   else
      [ -n "$is_qubes" ] || is_qubes=false
   fi

   if [ "$is_chroot" = "true" ]; then
      log notice "chroot: is_chroot=true"
      tb_settings_chroot_common
   else
      true "INFO: chroot: is_chroot=true is not set, ok."
   fi

   if test -f /run/qubes/this-is-templatevm ; then
      [ -n "$running_inside_qubes_template_vm" ] || running_inside_qubes_template_vm=true
   else
      [ -n "$running_inside_qubes_template_vm" ] || running_inside_qubes_template_vm=false
   fi

   if [ "$running_inside_qubes_template_vm" = "true" ] ; then
      tb_settings_qubes_common_templatevm
   else
      tb_settings_not_templatevm
   fi

   if [ "$tb_postinst" = "true" ]; then
      tb_settings_postinst_common
      if [ "$running_inside_qubes_template_vm" = "true" ] ; then
         tb_settings_qubes_postinst_templatevm
      fi
   fi

   true "tb_manual_run: $tb_manual_run"
   if [ "$tb_manual_run" = "true" ]; then
      tb_settings_manual_run_common
      if [ "$running_inside_qubes_template_vm" = "true" ]; then
         tb_settings_qubes_manual_run_templatevm
      fi
   fi

   if [ "$tb_user_home" = "" ]; then
      tb_user_home=~
      if [ "${tb_user_home}" = '/root' ] || [ "${tb_running_as_root}" = 'true' ]; then
         tb_user_home="$tb_global_binary_dir"
         tb_auto_set_user_home_msg="Automatically setting download folder to '$tb_user_home', because running as root."
      fi
   fi

   [ -n "$tb_home_folder" ] || tb_home_folder="$tb_user_home/$tb_install_folder_dot"
   [ -n "$tb_browser_folder" ] || tb_browser_folder="$tb_home_folder/$tb_browser_name"
   [ -n "$tb_cache_folder" ] || tb_cache_folder="$tb_user_home/.cache/$tb_install_folder"
   [ -n "$tb_temp_folder" ] || tb_temp_folder="$tb_cache_folder/temp"
   [ -n "$tb_downloaded_files_folder" ] || tb_downloaded_files_folder="$tb_cache_folder/files"
   [ -n "$tb_gpg_tmp_dir" ] || tb_gpg_tmp_dir="$tb_cache_folder/gpgtmpdir"
   [ -n "$tb_extract_temp_folder" ] || tb_extract_temp_folder="$tb_cache_folder/$tb_browser_name"

   [ -n "$tb_local_version_filename" ] || tb_local_version_filename="tbb_version.json"
   [ -n "$tb_local_version_file" ] || tb_local_version_file="$tb_browser_folder/Browser/$tb_local_version_filename"
   [ -n "$tbb_download_alpha_version" ] || tbb_download_alpha_version="false"

   [ -n "$TB_KEEP_OLD_VERSIONS_COUNT" ] || TB_KEEP_OLD_VERSIONS_COUNT="0"

   if [ ! "$tb_updater_run" = "true" ]; then
      true "tb_updater_run is set to: '$tb_updater_run'"
      log notice "Using '--postinst' option but outside of Qubes Template, skipping, ok."
      tb_exit_function 0
   fi

   ## Fortunately, on linux-image-486 kernel, while "uname --all" returns for example
   ## "Linux host 3.2.0-4-486 #1 Debian 3.2.41-2 i686 GNU/Linux",
   ## "uname --machine" returns "i686" and Tor Browser works fine with linux-image-486 kernel.
   ## (There are no 486 downloads for Tor Browser.)
   ##ARCH="x86_64"
   ##ARCH="i686"
   if [ "$ARCH" = "" ]; then
      true "INFO: Auto detecting ARCH..."
      ARCH="$(uname --machine)"
      log notice "ARCH '$ARCH' detected."
   else
      log notice "Skipping auto detecting ARCH because already set."
      log notice "ARCH is set to '$ARCH'."
   fi

   if [ "$ARCH_DOWNLOAD" = "" ]; then
      true "INFO: Auto detecting ARCH_DOWNLOAD..."
      if [ "$ARCH" = "i386" ]; then
         ARCH="i686"
      fi
      if printf '%s\n' "$ARCH" | grep -- "aarch64" >/dev/null 2>/dev/null ; then
         ARCH="arm64"
      fi

      if [ "$ARCH" = "x86_64" ]; then
         [ -n "$ARCH_DOWNLOAD" ] || ARCH_DOWNLOAD="linux-x86_64"
      elif [ "$ARCH" = "i686" ]; then
         [ -n "$ARCH_DOWNLOAD" ] || ARCH_DOWNLOAD="linux-i686"
      elif [ "$ARCH" = "arm64" ]; then
         [ -n "$ARCH_DOWNLOAD" ] || ARCH_DOWNLOAD="linux-arm64"
      else
         [ -n "$ARCH_DOWNLOAD" ] || ARCH_DOWNLOAD="linux-$ARCH"
         log warning "Guessing. Setting ARCH_DOWNLOAD to '$ARCH_DOWNLOAD'. If functional, this is probably OK."
      fi

      log notice "ARCH_DOWNLOAD '$ARCH_DOWNLOAD' detected."
   else
      log notice "Skipping auto detecting ARCH_DOWNLOAD because already set."
      log notice "ARCH_DOWNLOAD is set to '$ARCH_DOWNLOAD'."
   fi

   ## provides tbbversion function
   source /usr/libexec/tb-updater/version-validator

   if [ "$my_tty" = "" ]; then
      local my_tty_exit_code
      my_tty_exit_code="0"
      my_tty="$(tty)" || { my_tty_exit_code="$?" ; true; };
      if [ ! "$my_tty_exit_code" = "0" ]; then
         my_tty="none"
      fi
      ## Just in case.
      if [ "$my_tty" = "" ]; then
         my_tty="none"
      fi
   fi

   TITLE="$tb_title Downloader (by $tb_downloader_developers developers)"

   output_gui --icon "$ICON"
   output_gui --parenttty "$my_tty"
   output_gui --titlex "$TITLE"
   output_gui --titlecli "$TITLE"

   ## Make visible to curl-prgrs.
   export IDENTIFIER
   export who_ami

   if [ "${who_ami}" = 'tb-updater' ]; then
      noaskstart='true'
   fi

   ret="0"
   has curl.anondist-orig || { ret="$?" ; true; };

   if [ "$ret" = "0" ]; then
      ## using the non-uwt-wrapped version, if the uwt wrapper is installed,
      ## which is the case on a default Whonix installation
      CURL=curl.anondist-orig
   else
      ret="0"
      has curl || { ret="$?" ; true; };
      if [ "$ret" = "0" ]; then
         ## falling back to real curl, if the uwt wrapper has been uninstalled
         CURL=curl
      else
         local MSG="uwt_tool: Can not find curl. Please report this bug!"
         output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG"
         log error "$MSG"
         EXIT_CODE="1"
         return 0
      fi
   fi

   if [ "$CURL_PRGRS" = "" ]; then
      if [ "$ordinary" = "true" ]; then
         CURL_PRGRS="$CURL"
      elif [ -x /usr/libexec/helper-scripts/curl-prgrs ]; then
         ## Curl progress wrapper.
         CURL_PRGRS="/usr/libexec/helper-scripts/curl-prgrs"
      else
         true "/usr/libexec/helper-scripts/curl-prgrs not available, skipping."
         true "Setting CURL_PRGRS to $CURL."
         CURL_PRGRS="$CURL"
      fi
   fi

   ## Debugging CURL_PRGRS.
   #CURL_PRGRS="bash -x /usr/libexec/helper-scripts/curl-prgrs"

   ## Debugging / disabling CURL_PRGRS.
   #CURL_PRGRS="$CURL"

   ## Export CURL variable, so it can be read by $CURL_PRGRS.
   export CURL

   if [ "$tbb_download_alpha_version" = "true" ]; then
      [ -n "$TBB_RELEASE_CHANNEL" ] || TBB_RELEASE_CHANNEL="alpha"
   else
      [ -n "$TBB_RELEASE_CHANNEL" ] || TBB_RELEASE_CHANNEL="release"
   fi

   ## https://web.archive.org/web/20210603225307/https://dist.torproject.org/torbrowser/10.0.17/tor-browser-linux64-10.0.17_en-US.tar.xz
   [ -n "$TBB_DOWNLOAD_APPENDIX" ] || TBB_DOWNLOAD_APPENDIX=""
   [ -n "$tbb_versions_base_folder" ] || tbb_versions_base_folder="torbrowser/update_3"

   ## https://web.archive.org/web/20141004085451/https://www.torproject.org/projects/torbrowser/RecommendedTBBVersions file was later abolished by upstream
   ## https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/33521
   ## https://web.archive.org/web/20201210123314/https://www.torproject.org/projects/torbrowser/RecommendedTBBVersions/
   ## https://forums.whonix.org/t/update-torbrowser-does-not-see-version-10-0-6/10711
   ##
   ## example:
   ## http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/dist/torbrowser/10.0.6/tor-browser-linux64-10.0.6_en-US.tar.xz

   if [ "$tb_onion" = "true" ]; then
      ## dist.torproject.org has no corresponding onion address at time of writing.
      ## https://web.archive.org/web/20210424113513/https://securityheaders.com/?q=https%3A%2F%2Fdist.torproject.org%2F&followRedirects=on
      ##
      ## https://onion.torproject.org/
      ## https://web.archive.org/web/20210424113401/https://onion.torproject.org/
      ##
      ## https://securityheaders.com/?q=https%3A%2F%2Fwww.torproject.org&followRedirects=on
      ## https://web.archive.org/web/20210424113646/https://securityheaders.com/?q=https%3A%2F%2Fwww.torproject.org&followRedirects=on
      [ -n "$tbb_download_base_url" ] || tbb_download_base_url="http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion"
      [ -n "$TBB_REMOTE_FOLDER" ] || TBB_REMOTE_FOLDER="http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/dist/torbrowser"

      ## https://securityheaders.com/?q=https%3A%2F%2Faus1.torproject.org%2Ftorbrowser%2Fupdate_3%2Frelease%2Fdownloads.json&followRedirects=off
      ## https://web.archive.org/web/20210424113759/https://securityheaders.com/?q=https%3A%2F%2Faus1.torproject.org%2Ftorbrowser%2Fupdate_3%2Frelease%2Fdownloads.json&followRedirects=off
      ## Onion-Location	http://ot3ivcdxmalbsbponeeq5222hftpf3pqil24q3s5ejwo5t52l65qusid.onion/torbrowser/update_3/release/downloads.json
      [ -n "$tbb_versions_base_url" ] || tbb_versions_base_url="http://ot3ivcdxmalbsbponeeq5222hftpf3pqil24q3s5ejwo5t52l65qusid.onion"
      [ -n "$TBB_VERSIONS_FILE_LINK" ] || TBB_VERSIONS_FILE_LINK="${tbb_versions_base_url}/${tbb_versions_base_folder}/${TBB_RELEASE_CHANNEL}/download-${ARCH_DOWNLOAD}.json"
   else
      ## Download from the torproject.org clearnet by default.
      [ -n "$tbb_download_base_url" ] || tbb_download_base_url="https://www.torproject.org"
      [ -n "$TBB_REMOTE_FOLDER" ] || TBB_REMOTE_FOLDER="https://www.torproject.org/dist/torbrowser"
      [ -n "$CURL_FORCE_SSL" ] || CURL_FORCE_SSL="--tlsv1.3 --proto =https"

      [ -n "$tbb_versions_base_url" ] || tbb_versions_base_url="https://aus1.torproject.org"
      [ -n "$TBB_VERSIONS_FILE_LINK" ] || TBB_VERSIONS_FILE_LINK="${tbb_versions_base_url}/${tbb_versions_base_folder}/${TBB_RELEASE_CHANNEL}/download-${ARCH_DOWNLOAD}.json"
   fi

   if [ "$DEV_PASSTHROUGH" = "1" ]; then
      [ -n "$CURL_PROXY" ] || CURL_PROXY=()
   elif [ "$running_inside_qubes_template_vm" = "true" ] ; then
      ## Use Qubes updates proxy.
      if [ "$CURL_PROXY" = "" ]; then
         PROXY_SERVER='http://127.0.0.1:8082/'
         CURL_PROXY=( "--proxy" "$PROXY_SERVER" )
      fi
   elif [ -f /usr/share/whonix/marker ]; then
      ## sets: GATEWAY_IP
      eval "$(/usr/libexec/helper-scripts/settings_echo)"
      [ -n "$SOCKS_PORT_TBB_DOWNLOAD" ] || SOCKS_PORT_TBB_DOWNLOAD="9115"
      local random_socks_password
      ## https://spec.torproject.org/socks-extensions.html
      random_socks_password="$(random_alpha_numeric 43)"
      [ -n "$CURL_PROXY" ] || CURL_PROXY=(
         '--proxy' "socks5h://$GATEWAY_IP:$SOCKS_PORT_TBB_DOWNLOAD"
         '--proxy-user' "<torS0X>0:tb-updater_${random_socks_password}"
      )
   elif [ "${tb_onion}" = 'true' ]; then
      ## Onion download on non-Whonix.
      ##
      ## sets: GATEWAY_IP
      eval "$(/usr/libexec/helper-scripts/settings_echo)"
      ## Using port 9050 rather than 9115 here since Tor is likely not
      ## configured with a separate port for downloading Tor Browser.
      [ -n "$SOCKS_PORT_TBB_DOWNLOAD" ] || SOCKS_PORT_TBB_DOWNLOAD="9050"
      local random_socks_password
      ## https://spec.torproject.org/socks-extensions.html
      random_socks_password="$(random_alpha_numeric 43)"
      [ -n "$CURL_PROXY" ] || CURL_PROXY=(
         '--proxy' "socks5h://$GATEWAY_IP:$SOCKS_PORT_TBB_DOWNLOAD"
         '--proxy-user' "<torS0X>0:tb-updater_${random_socks_password}"
      )
   else
      [ -n "$CURL_PROXY" ] || CURL_PROXY=()
   fi

   log notice "CURL_PROXY: '${CURL_PROXY[*]}'"

   ## Also used by function tbbversion.
   [ -n "$RecommendedTBBVersions" ] || RecommendedTBBVersions="$tb_cache_folder/RecommendedTBBVersions"

   if [ -z "$tbb_version_last_downloaded_save_file" ]; then
      tbb_version_last_downloaded_save_file="$tb_cache_folder/tbb_version_last_downloaded_save_file"
      if [ "$who_ami" = 'tb-updater' ]; then
         ## Read the existing version from the system-wide location.
         tbb_version_last_downloaded_save_read_file="$tb_global_binary_dir/.cache/$tb_install_folder/tbb_version_last_downloaded_save_file"
      fi
   fi
   [ -n "$tbb_version_last_downloaded_save_read_file" ] || tbb_version_last_downloaded_save_read_file="$tbb_version_last_downloaded_save_file"

   if [ -f "$tbb_version_last_downloaded_save_read_file" ]; then
      [ -n "$tbb_version_previous_downloaded_version" ] || tbb_version_previous_downloaded_version="$(stcat "$tbb_version_last_downloaded_save_read_file")"
   fi
   [ -n "$tbb_version_previous_downloaded_version" ] || tbb_version_previous_downloaded_version="none"
   tbb_version_previous_downloaded_version_stripped="$(sanitize-string -- 20 "$tbb_version_previous_downloaded_version")"

   if ! test -f /usr/share/whonix/marker ; then
      [ -n "$TB_NO_TOR_CON_CHECK" ] || TB_NO_TOR_CON_CHECK="1"
   fi

   if [ ! "$tb_auto_set_user_home_msg" = "" ]; then
      log notice "$tb_auto_set_user_home_msg"
   fi
}

tb_create_folders() {
   mkdir --parents -- "$tb_home_folder"
   mkdir --parents -- "$tb_cache_folder"
   mkdir --parents -- "$tb_downloaded_files_folder"

   ## Required for /usr/libexec/helper-scripts/tor_bootstrap_check.bsh.
   TEMP_DIR="$tb_temp_folder"
   export TEMP_DIR
   safe-rm --recursive --force -- "$TEMP_DIR"
   mkdir --parents -- "$TEMP_DIR"
}

# tb_skip_if_higher_or_equal_version_already_downloaded() {
#    ## Do this only when using --postinst.
#    if [ ! "$tb_postinst" = "true" ]; then
#       true "INFO: Not using --postinst, therefore skipping ${FUNCNAME[0]}."
#       return 0
#    fi
#    if [ "$tbb_version_previous_downloaded_version_stripped" = "none" ]; then
#       true "INFO: Could not determine tbb_version_previous_downloaded_version_stripped (set to 'none'), therefore skipping ${FUNCNAME[0]}."
#       return 0
#    fi
#    if [ "$tbb_hardcoded_version" = "" ]; then
#       true "INFO: tbb_hardcoded_version is empty, therefore skipping ${FUNCNAME[0]}."
#       return 0
#    fi
#    if dpkg --compare-versions -- "$tbb_version_previous_downloaded_version_stripped" ge "$tbb_hardcoded_version" ; then
#       log notice "Previously downloaded version: '$tbb_version_previous_downloaded_version_stripped'"
#       log notice "Previously downloaded version is greater or equal tbb_hardcoded_version, good, ok."
#    fi
#
#    ## TODO: Does not belong here?
#    ## Being careful with deletion.
# #    if [ "$tb_downloaded_files_folder" = "$tb_global_binary_dir/.cache/$tb_install_folder/files" ]; then
# #       log notice "No need to keep older versions in Qubes root image. Saving disk space. Therefore deleting..."
# #       printf '%s\n' "safe-rm -r -f -- '$tb_downloaded_files_folder'"
# #       safe-rm -r -f -- "$tb_downloaded_files_folder"
# #       mkdir --parents -- "$tb_downloaded_files_folder"
# #    fi
# }

tb_connectivity_checks_interfaces() {
   if [ "$running_inside_qubes_template_vm" = "true" ] ; then
      ## Qubes Templates are non-networked as per Qubes default.
      return 0
   fi

   log notice "Running network interface available check... "
   if /usr/libexec/helper-scripts/check-network-access; then
      log notice "Network interface available."
      return 0
   fi

   MSG="<p><b>No network access.</b></p>

<p>Please check: <blockquote>Start menu -> System -> systemcheck
         <br></br>or in Terminal: systemcheck
         <br></br>or in Terminal with debugging: systemcheck -v</blockquote></p>

<p>Run systemcheck on Whonix-Gateway as well.</p>

<p>If systemcheck reports no problems with internet activity and downloading $tb_title still fails, please report a bug!</p>

<p>Debugging information:<br/>
/usr/libexec/helper-scripts/check-network-access non-zero exit code.</p>"

   output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

   output_cli error "$MSG"

   tb_exit_function 18
}

tb_connectivity_checks_tor() {
   if [ "$TB_NO_TOR_CON_CHECK" = "1" ]; then
      return 0
   fi

   if [ "$CURL_PROXY" = "" ]; then
      return 0
   fi

   if [ -x /usr/libexec/helper-scripts/tor_enabled_check ]; then
      log notice "Running Tor enabled check... "

      source /usr/libexec/helper-scripts/tor_enabled_check
      ## sets: TOR_ENABLED
      check_tor_enabled_do

      if [ "$TOR_ENABLED" = "0" ]; then
         MSG="<p><b>Tor not enabled yet.</b></p>

<p>Please check: <blockquote>Start menu -> System -> systemcheck
              <br></br>or in Terminal: systemcheck
              <br></br>or in Terminal with debugging: systemcheck -v</blockquote></p>

<p>Run systemcheck on Whonix-Gateway as well.</p>

<p>If systemcheck reports no problems with internet activity and downloading $tb_title still fails, please report a bug!</p>

<p>Debugging information:<br />
You could use <code>--no-tor-con-check</code> if you think function <code>${FUNCNAME[0]}</code> should be skipped.</p>"

         output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

         output_cli error "$MSG"

         tb_exit_function 5
      fi
      log notice "Tor enabled check: success"
   else
      true "/usr/libexec/helper-scripts/tor_enabled_check does not exist, skipping."
   fi

   if [ -x /usr/libexec/helper-scripts/tor_bootstrap_check.bsh ]; then
      log notice "Running Tor bootstrap check... "
      source /usr/libexec/helper-scripts/tor_bootstrap_check.bsh
      ## sets: tor_bootstrap_percent
      tb_run_function check_tor_circuit_established

      if [ ! "$tor_circuit_established" = "1" ]; then
         MSG="<p></b>Tor not fully bootstrapped.</b></p>

<p>Possible reasons:
<br></br>- no internet connectivity</p>

<p>Please check: <blockquote>Start menu -> System -> systemcheck
              <br></br>or in Terminal: systemcheck
              <br></br>or in Terminal with debugging: systemcheck -v</blockquote></p>

<p>Run systemcheck on Whonix-Gateway as well.</p>

<p>If systemcheck reports no problems with internet activity and downloading $tb_title still fails, please report a bug!</p>"

         output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

         output_cli error "$MSG"

         tb_exit_function 6
      fi
      log notice "Tor Bootstrap Check: success"
   else
      true "/usr/libexec/helper-scripts/tor_bootstrap_check.bsh not available, skipping."
   fi

   true "Tor fully bootstrapped."
}

tb_connectivity_checks_curl() {
   ## TODO: Maybe we should split the actual curl operation into a yet
   ## different user account? That would prevent a compromised curl from
   ## being able to affect later signature checks. Or maybe we need a
   ## general-purpose sandboxed curl wrapper.
   tb_notify_details="Checking connectivity... Will take a moment..."
   log notice "Running connectivity check...  Downloading...: '$tbb_download_base_url'"

   [ -n "$timeout_connectivity_checks_curl" ] || timeout_connectivity_checks_curl=180
   ## 1 MB = 1048576 bytes
   ## 2 MB = 2097152 bytes
   ## Export CURL_PRGRS_MAX_FILE_SIZE_BYTES, so $CURL_PRGRS can read it.
   export CURL_PRGRS_MAX_FILE_SIZE_BYTES="2097152"
   ## Export CURL_OUT_FILE, so $CURL_PRGRS can read it.
   export CURL_OUT_FILE="$tb_temp_folder/tbb_remote_folder"
   safe-rm --force -- "$CURL_OUT_FILE"
   curl_download_max_time="$timeout_connectivity_checks_curl"
   curl_download_target_url="$tbb_download_base_url"
   tb_download_common_exit_on_fail="false"
   tb_download_common

   ## Check if curl failed.
   if [ ! "$curl_exit_code" = "0" ]; then
      MSG="<p>$curl_download_target_url could not be reached.</p>

<p>Possible reasons:
<br></br>- $TBB_REMOTE_FOLDER is down
<br></br>- download location changed</p>

<p>Please check: <blockquote>Start menu -> System -> systemcheck
              <br></br>or in Terminal: systemcheck
              <br></br>or in Terminal with debugging: systemcheck -v</blockquote></p>

<p>If systemcheck reports no problems with internet activity and downloading $tb_title keeps failing, please report a bug!</p>

<p>(Debugging information: curl_status_message: $curl_status_message)</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 7
   fi

   log notice "Connectivity check succeeded."
}

tb_update_check() {
   if [ ! "$tbb_version_stripped" = "" ]; then
      true "INFO: tbb_version already set to '$tbb_version_stripped'. Skipping ${FUNCNAME[0]}, ok."
      return 0
   fi

   tb_notify_details="Checking $tb_title version... Will take a moment..."
   log notice "Find out latest version... Downloading...: '$TBB_VERSIONS_FILE_LINK'"

   [ -n "$timeout_version_file_download" ] || timeout_version_file_download=300
   ## 1 MB = 1048576 bytes
   ## 2 MB = 2097152 bytes
   ## Export CURL_PRGRS_MAX_FILE_SIZE_BYTES, so $CURL_PRGRS can read it.
   export CURL_PRGRS_MAX_FILE_SIZE_BYTES="2097152"
   ## Export CURL_OUT_FILE, so $CURL_PRGRS can read it.
   export CURL_OUT_FILE="$RecommendedTBBVersions"
   safe-rm --force -- "$CURL_OUT_FILE"
   curl_download_max_time="$timeout_version_file_download"
   curl_download_target_url="$TBB_VERSIONS_FILE_LINK"
   tb_download_common_exit_on_fail="8"
   tb_download_common

   test -f "$RecommendedTBBVersions"
}

tb_remote_version_parser() {
   if [ ! "$tbb_version_stripped" = "" ]; then
      true "INFO: tbb_version_stripped already set to '$tbb_version_stripped'. Simplified ${FUNCNAME[0]}, ok."
      [ -n "$tbb_recommended_versions_error" ] || tbb_recommended_versions_error=""
      return 0
   fi

   ## needs: $RecommendedTBBVersions
   ## sets: tbb_version_raw
   ## sets: tbb_version_stripped
   ## sets: tbb_recommended_versions_error
   tb_run_function tbbversion
}

tb_remote_version_sanity_test() {
   if [ "$tbb_version_stripped" = "UNKNOWN" ]; then
      download_fail_help_set
      local MSG="<p>$installed_or_not_text</p>

<p>Could not find out latest <code>$tb_title</code> version! <code>$tbb_recommended_versions_error</code></p>

<p>Either <code>$TBB_VERSIONS_FILE_LINK</code> is invalid or this is a <code>$SCRIPTNAME</code> (by <code>$tb_downloader_developers</code> developers) bug.</p>

<p>$DOWNLOAD_FAIL_HELP</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 9
   fi

   if dpkg --compare-versions -- "$tbb_version_stripped" lt "$tbb_hardcoded_version" ; then
      download_fail_help_set
      local MSG="<p>$installed_or_not_text</p>

<p>Detected latest <code>$tb_title</code> version <code>$tbb_version_stripped</code> is older than hardcoded known-good version <code>$tbb_hardcoded_version</code>!</p>

<p>Either <code>$TBB_VERSIONS_FILE_LINK</code> is invalid or this is a <code>$SCRIPTNAME</code> (by <code>$tb_downloader_developers</code> developers) bug.</p>

<p>$DOWNLOAD_FAIL_HELP</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 9
   elif dpkg --compare-versions -- "$tbb_version_stripped" gt "$tbb_hardcoded_version" ; then
       true "OK"
   elif dpkg --compare-versions -- "$tbb_version_stripped" eq "$tbb_hardcoded_version" ; then
       true "OK"
   else
      download_fail_help_set
      local MSG="<p>$installed_or_not_text</p>

<p>Detected latest <code>$tb_title</code> version: <code>$tbb_version_stripped</code>
hardcoded known-good version: <code>$tbb_hardcoded_version</code>
dpkg --compare-versions failed!</p>

<p>Either <code>$TBB_VERSIONS_FILE_LINK</code> is invalid or this is a <code>$SCRIPTNAME</code> (by <code>$tb_downloader_developers</code> developers) bug.</p>

<p>$DOWNLOAD_FAIL_HELP</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 9
   fi
}

tb_local_version_detection() {
   ## sets: tbb_hardcoded_version
   true "INFO: tbb_download_alpha_version: $tbb_download_alpha_version"
   if [ "$tbb_download_alpha_version" = "true" ]; then
      ## sets: tbb_hardcoded_version
      source /usr/share/tb-updater/tbb_hardcoded_version_alpha
      log notice "Using alpha version. See:
$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Alpha"
   else
      ## sets: tbb_hardcoded_version
      source /usr/share/tb-updater/tbb_hardcoded_version
      log notice "Using stable version. For alpha version, see: $tb_documentation_base_url_clearnet/wiki/$tb_wiki#Alpha"
   fi

   ## Used by function tbbversion_installed
   if [ -z "$tbb_folder" ]; then
      if [ "$who_ami" = 'tb-updater' ]; then
         ## We install the browser into the tb-updater home folder, but it will
         ## be moved into a system-wide location later. Thus we need to check
         ## the system-wide location to see if the browser is already
         ## installed and what version it's at.
         tbb_folder="$tb_global_binary_dir/$tb_install_folder_dot/$tb_browser_name"
         tb_local_version_file="$tbb_folder/Browser/$tb_local_version_filename"
      else
         tbb_folder="$tb_browser_folder"
         ## tb_local_version_file set earlier by function tb_preparation.
      fi
   fi

   ## sets: tbb_locally_installed_version_stripped
   ## sets: tbb_locally_installed_version_detect_success
   tb_run_function tbbversion_installed

   if [ -d "$tbb_folder" ]; then
      installed_or_not_result="true"
      installed_or_not_text=""
   else
      installed_or_not_result="false"
      installed_or_not_text="$tb_title is currently not installed.
(Folder $tbb_folder does not exist.)"
   fi
}

tb_confirm_update() {
   if [ "$running_inside_qubes_template_vm" = "true" ] ; then
      local folder_name found
      for folder_name in "/home/$who_ami/$tb_install_folder_dot" "/home/$who_ami/.cache/${tb_install_folder}" ; do
         if [ -d "$folder_name" ]; then
            found=true
            break
         fi
      done
      if [ "$found" = "true" ]; then
         local MSG="\
<p>Obsolete folders exist. It is recommended to delete them. \
Otherwise updating <code>$tb_title</code> in this Template will not result in newly created App Qubes to inherit the updated version of <code>$tb_title</code>.
<br />
<br />To delete these obsolete folders, please run:
<br />
<br /><code>safe-rm -r -f -- '/home/$who_ami/$tb_install_folder_dot'</code>
<br /><code>safe-rm -r -f -- '/home/$who_ami/.cache/${tb_install_folder}'</code>
<br />
<br />(Or delete them using a file manager.)
<br />
<br />More info: <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Qubes-specific>$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Qubes-specific</a></p>"
         output_gui ${output_opts[@]} --messagex --typex "info" --message "$MSG" --done

         output_cli notice "$MSG"
      fi
   fi

   log notice "Previously downloaded version: '$tbb_version_previous_downloaded_version_stripped'"
   log notice "Currently installed version  : '$tbb_locally_installed_version_stripped'"
   log notice "Online detected version      : '$tbb_version_stripped'"

   local MSG question button type
   button="yesno"
   question="Download now?"

   local updater_vs_downloader_msg confirmation_screen_learn_more_link

   updater_vs_downloader_msg="\
If you would like to keep your browser profile and update rather than re-downloading $tb_title, \
you must use \
<a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Internal_Updater>$tb_title's internal updater</a>. \
In that case, say no now.<br></br>
<br></br>
This program \
(<a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Downloader>$tb_title Downloader (by $tb_downloader_developers developers)</a>) \
is incapable of keeping user data.<br></br>
<br></br>
YOUR BROWSER WILL BE KILLED.<br></br>
YOUR OLD BROWSER PROFILE INCLUDING BOOKMARKS AND PASSWORDS WILL GET DELETED."

   confirmation_screen_learn_more_link="\
<a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Download_Confirmation_Notification>Learn more about this Download Confirmation Notification.</a>"

   if [ "$running_inside_qubes_template_vm" = "true" ] ; then
      qubes_running_inside_template_vm_msg="\
<u>Qubes specific notice</u>: Currently running inside a Template.
Due to <a href=$tb_documentation_base_url_clearnet/wiki/Dev/Qubes#tb-updater_vs_Template>technical limitations</a>,
running this program in this Template will not update $tb_title in App Qubes, which are based on this Template.
Only newly created App Qubes, which are based on this Template will benefit from this.
To update $tb_title inside existing App Qubes, please update $tb_title inside App Qubes."
   fi

   if [ "$installed_or_not_result" = "true" ]; then
      type="warning"
      if [ "$tbb_locally_installed_version_detect_success" = "false" ]; then
         ## installed, but local version number unknown
         MSG="\
<p>$updater_vs_downloader_msg</p>
<p>$confirmation_screen_learn_more_link</p>
<p>$qubes_running_inside_template_vm_msg</p>"
      else
         ## installed, local version number known
         if dpkg --compare-versions -- "$tbb_version_stripped" eq "$tbb_locally_installed_version_stripped"; then
            up_to_date_or_not_text="Looks like $tb_title is already up to date."
            re_install_or_install_text="Please close $tb_title if you want to (re-)install!"
	          if [ "$UPDATE_ONLY_IF_NEWER" = "1" ]; then
               true "INFO: UPDATE_ONLY_IF_NEWER is set 1."
               log notice "Skip downloading: current '$tb_title' '$tbb_locally_installed_version_stripped' is already up-to-date."
               tb_exit_function 0
	          fi
         elif dpkg --compare-versions -- "$tbb_version_stripped" gt "$tbb_locally_installed_version_stripped"; then
            up_to_date_or_not_text="Looks like there is an upgrade for $tb_title."
            re_install_or_install_text="Please close $tb_title if you want to (re-)install!"
         elif dpkg --compare-versions -- "$tbb_version_stripped" lt "$tbb_locally_installed_version_stripped"; then
            up_to_date_or_not_text="<b><u>WARNING: Looks like a downgrade attack!</u></b>"
            re_install_or_install_text="Please close $tb_title if you want to (re-)install!"
         else
            error "Failed to compare versions using dpkg.
<br></br>
<br></br>tbb_locally_installed_version_detect_success: $tbb_locally_installed_version_detect_success"
            tb_exit_function 1
         fi
         MSG="\
<p>$up_to_date_or_not_text</p>

<p>$re_install_or_install_text</p>

<p>If your currently installed version is:<blockquote>
   - higher: you are likely target of a downgrade attack, SAY NO NOW.<br></br>
   - equal : only proceed, if you want to create a new browser profile.<br></br>
   - lower : you should upgrade.</blockquote></p>

<p>$updater_vs_downloader_msg</p>
<p>$confirmation_screen_learn_more_link</p>"
      fi

      output_cli notice "$MSG"
   else
      ## not installed
      type="info"
      MSG="\
<p>$confirmation_screen_learn_more_link</p>"

      if ! [ "$tb_postinst" = "true" ]; then
         output_cli notice "$MSG"
      fi
   fi

   if ! [ "$tb_postinst" = "true" ]; then
      printf '%s\n' "$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Download_Confirmation_Notification"
   fi

   if [ "$TB_INPUT" = "none" ]; then
      true "INFO: TB_INPUT set to none, ok."
   else
      local answer
      if [ "$TB_INPUT" = "stdin" ]; then
         log question "$question
y/n?"
         read -r answer
         if [ ! "$answer" = "y" ]; then
            log notice "Canceled. Exit."
            tb_exit_function 10
         fi
      else
         #printf '%s\n' "/usr/libexec/msgcollector/tb_updater_gui \"$type\" \"$TITLE\" \"$tbb_locally_installed_version_stripped\" \"$tbb_recommended_versions_comma_separated\" \"$MSG\" \"$question\" \"$button\")"

         answer="$("/usr/libexec/msgcollector/tb_updater_gui" "$type" "$TITLE" "$tbb_locally_installed_version_stripped" "$tbb_version_stripped" "$MSG" "$question" "$button")"
         if [ "$answer" = "65536" ]; then ## Button 'Yes' has not been pressed.
            log notice "Canceled. Exit."
            tb_exit_function 10
         fi
      fi
   fi
}

tb_version_processing() {
   if [ "$tb_hard_reset" == "true" ]; then
      tbb_version_stripped="$tbb_version_previous_downloaded_version_stripped"
   fi

   if [ "$tbb_version_stripped" = "" ]; then
      log error "in function tb_version_processing: variable $tbb_version_stripped not set!"
      tb_exit_function 17
   fi

   [ -n "$tbb_version_folder" ] || tbb_version_folder="$tbb_version_stripped"
   [ -n "$tb_browser_download_name" ] || tb_browser_download_name="tor-browser"
   [ -n "$TBB_EXTRA_FOLDER" ] || TBB_EXTRA_FOLDER="${tbb_version_folder}"

   [ -n "$TBB_SIG_FILENAME" ] || TBB_SIG_FILENAME="${tb_browser_download_name}-${ARCH_DOWNLOAD}-${tbb_version_stripped}.tar.xz.asc"
   [ -n "$TBB_PACKAGE_FILENAME" ] || TBB_PACKAGE_FILENAME="${tb_browser_download_name}-${ARCH_DOWNLOAD}-${tbb_version_stripped}.tar.xz"

   [ -n "$TBB_SIG_LINK" ] || TBB_SIG_LINK="${TBB_REMOTE_FOLDER}/${TBB_EXTRA_FOLDER}/${TBB_SIG_FILENAME}${TBB_DOWNLOAD_APPENDIX}"
   [ -n "$TBB_SIG_FULL_PATH" ] || TBB_SIG_FULL_PATH="$tb_downloaded_files_folder/$TBB_SIG_FILENAME"

   [ -n "$TBB_PACKAGE_LINK" ] || TBB_PACKAGE_LINK="$TBB_REMOTE_FOLDER/${TBB_EXTRA_FOLDER}/$TBB_PACKAGE_FILENAME${TBB_DOWNLOAD_APPENDIX}"
   [ -n "$TBB_PACKAGE_FULL_PATH" ] || TBB_PACKAGE_FULL_PATH="$tb_downloaded_files_folder/$TBB_PACKAGE_FILENAME"
}

tb_reset() {
   if [ ! "$tb_hard_reset" == "true" ]; then
      return 0
   fi

   log notice "--reset. Performing hard reset rather than download. See: $tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Tor_Browser_Hard_Reset"

   if ! test -f "$TBB_PACKAGE_FULL_PATH" ; then
      local MSG="\
<p><b>Tor Browser archive file <code>$TBB_PACKAGE_FULL_PATH</code> not yet downloaded. Run without <code>--reset</code> is required.</b>
<br/>
<br/>See:
<br/><a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Tor_Browser_Hard_Reset>$tb_documentation_base_url_clearnet/wiki/$tb_wiki/Advanced_Users#Tor_Browser_Hard_Reset</a></p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 16
   fi
}

tb_kill_already_running_tb_maybe() {
   if [ ! "$NOKILLTB" = "1" ]; then
      [ -n "$tb_process_name" ] || tb_process_name="firefox.real"

      log notice "Because you are not using --nokilltb, now killing potentially still running instances of '$tb_title'..."
      killall "$tb_process_name" || true
   fi
}

tb_download_common() {
   if [ "$TB_FORCE_INSTALL" = "1" ]; then
      true
   else
      progressbaridx="$(cat -- "/proc/sys/kernel/random/uuid")"
      tb_notify_msg="Download
----------------------------------------------------------------------
$tb_notify_details"
      if [ "$TB_INPUT" = "" ] || [ "$TB_INPUT" = "gui" ]; then
         output_gui ${output_opts[@]} --progressbaridx "$progressbaridx" --progressbarx --parentpid "$$" --typex "info" --progressbartitlex "$TITLE" --message "$tb_notify_msg" --parentpid "$$" --done
         ## $CURL_PRGRS honors the $CURL and the $CURL_PRGRS_EXEC environment
         ## variables. (See above.)
         ## Define what CURL_PRGRS is supposed to eval.
         ## ($percent is a local variable provided by $CURL_PRGRS.)
         export -f output_gui
         CURL_PRGRS_EXEC="output_gui ${output_opts[*]} --progressbaridx $progressbaridx --progressx"
         export CURL_PRGRS_EXEC
      fi
   fi

   log notice "CURL_OUT_FILE: '$CURL_OUT_FILE'"

   ## Debugging: Simulating endless data attack.
   #export CURL_PRGRS_MAX_FILE_SIZE_BYTES="1048576"

   curl_exit_code="0"
   $CURL_PRGRS \
      $CURL_RESUME \
      --fail \
      "${CURL_PROXY[@]}" \
      $CURL_FORCE_SSL \
      --retry-connrefused \
      --retry 10 \
      --retry-delay 60 \
      --max-time "$curl_download_max_time" \
      --location \
      $CURL_OPTS \
      --output "$CURL_OUT_FILE" \
      "$curl_download_target_url" \
      &

   last_pid_list="$!"
   wait "$last_pid_list" || { curl_exit_code="$?" ; true; };
   last_pid_list=""

   if [ "$progressbaridx" = "" ]; then
      true
   else
      output_gui ${output_opts[@]} --progressbaridx "$progressbaridx" --progressx "100" || true
      progressbaridx=""
   fi

   if [ -x "/usr/libexec/helper-scripts/curl_exit_codes" ]; then
      curl_status_message="$(/usr/libexec/helper-scripts/curl_exit_codes "$curl_exit_code")" || true
   else
      curl_status_message="$curl_exit_code"
   fi

   if [ "$tb_download_common_exit_on_fail" = "false" ]; then
      true "${FUNCNAME[0]} is not supposed to exit, because tb_download_common_exit_on_fail is set to $tb_download_common_exit_on_fail."
      return 0
   fi

   ## Check if curl failed.
   if [ ! "$curl_exit_code" = "0" ]; then
      download_fail_help_set
      MSG="<p>Failed to download: $curl_download_target_url</p>

<p>$DOWNLOAD_FAIL_HELP</p>

<p>(Debugging information: curl_status_message: $curl_status_message)</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function "$tb_download_common_exit_on_fail"
   fi

   true "INFO: Done, downloaded: $curl_download_target_url"
}

tb_download_files() {
   ### signature ###

   tb_notify_details="Downloading $tb_title digital signature (GPG)... Will take a moment..."
   log notice "Digital signature (GPG) download... Will take a moment..."
   log notice "Downloading...: '$TBB_SIG_LINK'"

   [ -n "$timeout_signature_file_download" ] || timeout_signature_file_download=300
   ## 1 MB = 1048576 bytes
   ## 2 MB = 2097152 bytes
   ## Export CURL_PRGRS_MAX_FILE_SIZE_BYTES, so $CURL_PRGRS can read it.
   export CURL_PRGRS_MAX_FILE_SIZE_BYTES="2097152"
   ## Export CURL_OUT_FILE, so $CURL_PRGRS can read it.
   export CURL_OUT_FILE="$TBB_SIG_FULL_PATH"
   safe-rm --force -- "$CURL_OUT_FILE"
   curl_download_max_time="$timeout_signature_file_download"
   curl_download_target_url="$TBB_SIG_LINK"
   tb_download_common_exit_on_fail="8"
   tb_download_common

   ### archive file ###

   tb_notify_details="Downloading $tb_title... Will take a while..."
   log notice "Downloading '$tb_title'..."
   log notice "Downloading...: '$TBB_PACKAGE_LINK'"

   [ -n "$timeout_archive_file_download" ] || timeout_archive_file_download=3600
   ## 1 MB = 1048576 bytes
   ## 200 MB = 209715200 bytes
   ## Export CURL_PRGRS_MAX_FILE_SIZE_BYTES, so $CURL_PRGRS can read it.
   export CURL_PRGRS_MAX_FILE_SIZE_BYTES="209715200"
   ## Export CURL_OUT_FILE, so $CURL_PRGRS can read it.
   export CURL_OUT_FILE="$TBB_PACKAGE_FULL_PATH"
   ## Not deleting to support resume.
   #safe-rm --force -- "$CURL_OUT_FILE"
   curl_download_max_time="$timeout_archive_file_download"
   curl_download_target_url="$TBB_PACKAGE_LINK"
   tb_download_common_exit_on_fail="17"
   tb_download_common

   tb_download_attempt_success="true"
}

tb_gpg_verify() {
   log notice "Digital signature (GPG) verification... This will take a moment..."

   log notice "Using digital signature signing key by The Tor Project."
   gpg_bash_lib_input_key_import_dir="/usr/share/torbrowser-updater-keys.d"

   gpg_bash_lib_input_verify_timeout_after="60"
   gpg_bash_lib_input_verify_kill_after="10"
   gpg_bash_lib_input_temp_folder="$tb_gpg_tmp_dir"
   gpg_bash_lib_input_data_file="$TBB_PACKAGE_FULL_PATH"
   gpg_bash_lib_input_sig_file="$TBB_SIG_FULL_PATH"
   gpg_bash_lib_input_file_name_enforce="false"
   gpg_bash_lib_input_cleanup="false"
   gpg_bash_lib_input_error_handler_extra='tb_error_handler "$gpg_bash_lib_output_error_handler_message"'
   ## One month has 2592000 seconds.
   ## (60 [seconds] * 60 [minutes] * 24 [hours] * 30 [days])
   ## Setting this to 3 months. (777600 seconds)
   ## (2592000 * 3 [months])
   [ -n "$gpg_bash_lib_input_maximum_age_in_seconds" ] || gpg_bash_lib_input_maximum_age_in_seconds="2592000"

   gpg_bash_lib_function_main_verify

   gpg_bash_lib_output_diagnostic_message="$(/usr/libexec/msgcollector/br_add "$gpg_bash_lib_output_diagnostic_message")"

   ## Not checking for gpg_bash_lib_output_alright_status, because then there
   ## would be no way to accept outdated signatures. It is not clear if it is
   ## guaranteed to have a new stable TBB release every three months.
   if [ ! "$gpg_bash_lib_output_validsig_status" = "true" ] || [ "$gpg_bash_lib_output_failure" = "true" ] ; then
      local MSG="<p><b>Digital signature (GPG) could NOT be verified.</b>
<br></br>$tb_title update failed! Try again later.</p>

<p>gpg_bash_lib_output_alright_status: <code>$gpg_bash_lib_output_alright_status</code>
<br></br>gpg_bash_lib_output_failure: <code>$gpg_bash_lib_output_failure</code></p>

<p>gpg_bash_lib_output_diagnostic_message:</p>

<p>$gpg_bash_lib_output_diagnostic_message</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 12
   fi

   log notice "Digital signature (GPG) verification ok."
}

tb_confirm_install() {
   if [ "$tb_confirm_installation_skip" = "true" ]; then
      return 0
   fi

   local MSG question button answer type clock_hint
   local signature_freshness_msg signature_creation_msg
   local last_used_gpg_bash_lib_output_signed_on_date
   local last_used_gpg_bash_lib_output_signed_on_unixtime
   local real_cache_folder

   if [ "${who_ami}" = 'tb-updater' ]; then
      real_cache_folder="${tb_global_binary_dir}/.cache/$tb_install_folder"
   else
      real_cache_folder="${tb_cache_folder}"
   fi

   if [ -f "$real_cache_folder/last_used_gpg_bash_lib_output_signed_on_date" ]; then
      last_used_gpg_bash_lib_output_signed_on_date="$(cat "$real_cache_folder/last_used_gpg_bash_lib_output_signed_on_date")" || true
   fi
   if [ "$last_used_gpg_bash_lib_output_signed_on_date" = "" ]; then
      last_used_gpg_bash_lib_output_signed_on_date="Unknown. Probably never downloaded a signature before."
   fi
   if [ -f "$real_cache_folder/last_used_gpg_bash_lib_output_signed_on_unixtime" ]; then
      last_used_gpg_bash_lib_output_signed_on_unixtime="$(cat -- "$real_cache_folder/last_used_gpg_bash_lib_output_signed_on_unixtime")" || true
   fi
   if [ "$last_used_gpg_bash_lib_output_signed_on_unixtime" = "" ]; then
      last_used_gpg_bash_lib_output_signed_on_unixtime="unknown"
   fi

   ## Thanks to:
   ## https://mywiki.wooledge.org/BashFAQ/054
   if [[ "$last_used_gpg_bash_lib_output_signed_on_unixtime" != *[!0-9]* ]]; then
      true "'$last_used_gpg_bash_lib_output_signed_on_unixtime' is strictly numeric."
      if [ "$last_used_gpg_bash_lib_output_signed_on_unixtime" -lt "$gpg_bash_lib_output_signed_on_unixtime" ]; then
         signature_freshness_msg="The downloaded signature is newer than the last known signature as expected."
         type="info"
      elif [ "$last_used_gpg_bash_lib_output_signed_on_unixtime" -gt "$gpg_bash_lib_output_signed_on_unixtime" ]; then
         signature_freshness_msg="<b>You are likely target of a downgrade attack</b>, SAY NO NOW! The downloaded signature is older than the last known signature. \
Unless you are downgrading, you should abort now and try again later!"
         type="warning"
      elif [ "$last_used_gpg_bash_lib_output_signed_on_unixtime" = "$gpg_bash_lib_output_signed_on_unixtime" ]; then
         signature_freshness_msg="<b>You could be target of an indefinite freeze attack!</b> The downloaded signature has the same creation date as the last known signature. \
Unless you are re-installing the same version, you should abort now and try again later!"
         type="warning"
      else
         error "last_used_gpg_bash_lib_output_signed_on_unixtime $last_used_gpg_bash_lib_output_signed_on_unixtime \
neither -lt, -gt nor equals gpg_bash_lib_output_signed_on_unixtime $gpg_bash_lib_output_signed_on_unixtime"
         type="error"
      fi
   else
      signature_freshness_msg="We have not previously accepted a signature yet. Therefore assisted check for downgrade \
or indefinite freeze attacks skipped. Please check the Current Signature Creation Date looks sane."
      type="info"
   fi

   if [ -f /usr/share/whonix/marker ]; then
      clock_hint="In that case, please check your clock is correct."
   fi

   case "$gpg_bash_lib_output_freshness_detail" in
      "lenient")
         signature_creation_msg="<b>Your clock might be slow.</b> $clock_hint
<br></br>
<br></br>According to your system clock, the signature was created $gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty before current time.
You can probably ignore this, because it still is within range. (Okay up to $gpg_bash_lib_output_maximum_age_pretty_output before.)"
         ;;
      "slow")
         signature_creation_msg="<b>Your clock might be slow.</b> $clock_hint
<br></br>
<br></br>According to your system clock, the signature was created $gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty before current time."
         ;;
      "outdated")
         signature_creation_msg="<b>The signature looks quite old already.</b>
<br></br>
<br></br>Either,
<br></br>- your clock might be fast (at least $gpg_bash_lib_output_in_future_pretty_output fast). $clock_hint
<br></br>- there is really no newer signature yet. The signature is really older than $gpg_bash_lib_output_maximum_age_pretty_output already. (Older than $gpg_bash_lib_output_in_future_pretty_output already.)
<br></br>- this is a $SCRIPTNAME bug
<br></br>- this is an attack"
         ;;
      "current")
         signature_creation_msg="According to your system clock, the signature was created $gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime_pretty ago."
         ;;
      *)
         error "gpg_bash_lib_output_freshness_detail is neither lenient, nor slow, nor outdated, nor current, it is: $gpg_bash_lib_output_freshness_detail"
         return 0
         ;;
   esac

   gpg_bash_lib_output_gpg_verify_output_br_added="$(/usr/libexec/msgcollector/br_add "$gpg_bash_lib_output_gpg_verify_output")"

   MSG="<p><b>Installation confirmation</b></p><p><table><tr>
<td>Currently installed version:</td> <td><tt> <code>$tbb_locally_installed_version_stripped</code></tt></td></tr><tr>
<td>Downloaded version         :</td> <td><tt> <code>$tbb_version_stripped</code></tt></td></tr></table></p>
<p>$signature_freshness_msg</p><p><table><tr>
<td>Previous Signature Creation Date:</td> <td><tt> <code>$last_used_gpg_bash_lib_output_signed_on_date</code></tt></td></tr><tr>
<td>Last Signature Creation Date    :</td> <td><tt> <code>$gpg_bash_lib_output_signed_on_date</code></tt></td></tr></table></p>
<p>$signature_creation_msg</p>
<p><u>gpg reports</u>:<br></br>
$gpg_bash_lib_output_gpg_verify_output_br_added</p>
<p><a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Installation_Confirmation_Notification>Learn more about this Installation Confirmation Notification.</a></p>"

   output_cli notice "$MSG"

   printf '%s\n' "$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Installation_Confirmation_Notification"

   button="yesno"
   question="Install now?"

   if [ "$TB_INPUT" = "none" ]; then
      true "INFO: TB_INPUT is set to '$TB_INPUT', continuing without asking."
   else
      if [ "$TB_INPUT" = "stdin" ]; then
         log question "QUESTION: $question
y/n?"
         read -r answer
         if [ ! "$answer" = "y" ]; then
            log notice "Canceled. Exit."
            tb_exit_function 14
         fi
      else
         answer="$(/usr/libexec/msgcollector/generic_gui_message "$type" "$TITLE" "$MSG" "$question" "$button")"
         if [ ! "$answer" = "16384" ]; then ## Button 'Yes' has not been pressed.
            log notice "Canceled. Exit."
            tb_exit_function 14
         fi
      fi
   fi

   ## Intentionally using tb_cache_folder and not real_cache_folder here. If
   ## running under sandbox-update-torbrowser, we will have no permission to
   ## write to real_cache_folder directly. sandbox-update-torbrowser will move
   ## these files into real_cache_folder later.
   overwrite "$tb_cache_folder/last_used_gpg_bash_lib_output_signed_on_date" "$gpg_bash_lib_output_signed_on_date"
   overwrite "$tb_cache_folder/last_used_gpg_bash_lib_output_signed_on_unixtime" "$gpg_bash_lib_output_signed_on_unixtime"
}

tb_extract() {
   if [ "$TB_NO_EXTRACT" = "true" ]; then
      true "INFO: Skipping ${FUNCNAME[0]}, because TB_NO_EXTRACT is 'true', ok."
      return 0
   fi

   if [ "$TB_FORCE_INSTALL" != "1" ]; then
      progressbaridx="$(cat -- "/proc/sys/kernel/random/uuid")"
      tb_notify_msg="Extraction
----------------------------------------------------------------------
Extracting $tb_title... This could take a moment..."
      if [ "$TB_INPUT" = "" ] || [ "$TB_INPUT" = "gui" ]; then
         output_gui ${output_opts[@]} --progressbaridx "$progressbaridx" --progressbarx --parentpid "$$" --typex "info" --progressbartitlex "$TITLE" --message "$tb_notify_msg" --parentpid "$$" --done
      fi
   fi

   local timeout_after kill_after file_name
   timeout_after="180"
   kill_after="30"
   file_name="$TBB_PACKAGE_FULL_PATH"

   log notice "Extracting '$file_name'..."

   ## Folder must be already exist, otherwise `tar` would fail.
   mkdir --parents -- "$tb_extract_temp_folder"

   ## Debugging.
   #timeout_after="0.001"
   #kill_after="0.001"

   ## `tar` vs `bsdtar`
   ##
   ## Using `bsdtar` because it can auto detect the archive type.
   ## When using `tar` in a pipe, we would need to explicitly set '--xz'.
   ##
   ## Using '--strip-components 1':
   ## - Because we are also using '--directory "$tb_extract_temp_folder"'
   ## - To remove language specific default folder names such as tor-browser_en-US.

   tb_tar_exit_code="0"

   if [ "$TB_FORCE_INSTALL" = "1" ]; then
      timeout \
         --kill-after="$kill_after" \
         "$timeout_after" \
            bsdtar \
               --strip-components 1 \
               --extract \
               --directory "$tb_extract_temp_folder" \
               --file "$file_name" \
               &

      lastpid="$!"
      true "INFO: wait bsdtar lastpid"
      wait "$lastpid" || { tb_tar_exit_code="$?" ; true; };
   else
     ## pv_echo_command is parsed using `eval`.
     # shellcheck disable=SC2089
      pv_echo_command='echo "extraction percent done: "$percent" / 100" >&2'
      # shellcheck disable=SC2090
      export pv_echo_command
      export -f output_gui
     ## pv_wrapper_command is parsed using `eval`.
     # shellcheck disable=SC2089
      pv_wrapper_command="output_gui ${output_opts[*]} --progressbaridx $progressbaridx --progressx \"\$percent\""
      # shellcheck disable=SC2090
      export pv_wrapper_command

      local tar_fifo tar_pid pv_wrapper_fifo pv_wrapper_fifo
      local pv_exit_code tar_exit_code pv_wrapper_exit_code
      tar_fifo="$TEMP_DIR/tar_fifo"
      pv_wrapper_fifo="$TEMP_DIR/pv_wrapper_fifo"
      pv_exit_code="0"
      tar_exit_code="0"
      pv_wrapper_exit_code="0"

      safe-rm --force -- "$tar_fifo"
      safe-rm --force -- "$pv_wrapper_fifo"
      mkfifo -- "$tar_fifo"
      mkfifo -- "$pv_wrapper_fifo"

      timeout \
         --kill-after="$kill_after" \
         "$timeout_after" \
            bsdtar \
               --strip-components 1 \
               --extract \
               --directory "$tb_extract_temp_folder" \
               --file \
               - \
               < "$tar_fifo" \
               &
      tar_pid="$!"
      last_pid_list+=" $tar_pid"

      ## Debugging.
      #export pv_wrapper_debug=true

      timeout \
         --kill-after="$kill_after" \
         "$timeout_after" \
            bash \
               /usr/libexec/msgcollector/pv_wrapper \
               < "$pv_wrapper_fifo" \
               &
      pv_wrapper_pid="$!"
      last_pid_list+=" $pv_wrapper_pid"

      timeout \
         --kill-after="$kill_after" \
         "$timeout_after" \
            pv \
               --numeric \
               "$file_name" \
               1> "$tar_fifo" \
               2> "$pv_wrapper_fifo" \
               &
      pv_pid="$!"
      last_pid_list+=" $pv_pid"

      true "INFO: wait pv_pid"
      wait "$pv_pid" || { pv_exit_code="$?" ; true; };
      true "INFO: wait tar_pid"
      wait "$tar_pid" || { tar_exit_code="$?" ; true; };
      true "INFO: wait pv_wrapper_pid"
      wait "$pv_wrapper_pid" || { pv_wrapper_exit_code="$?" ; true; };

      if [ ! "$pv_exit_code" = "0" ]; then
         tb_tar_exit_code="1"
      fi
      if [ ! "$tar_exit_code" = "0" ]; then
         tb_tar_exit_code="1"
      fi
      if [ ! "$pv_wrapper_exit_code" = "0" ]; then
         tb_tar_exit_code="1"
      fi
   fi

   ## `timeout` returns:
   ## - 124 if sigterm was sufficient
   ## - 137 if needed to use kill.

   if [ "$progressbaridx" = "" ]; then
      true
   else
      output_gui ${output_opts[@]} --progressbaridx "$progressbaridx" --progressx "100" || true
      progressbaridx=""
   fi

   if [ ! "$tb_tar_exit_code" = "0" ]; then
      ## Debugging.
      ls -la "$tb_extract_temp_folder" || true

      local MSG="<p><b>Could not extract <code>$file_name</code>!</b>
<br></br>
<br></br>(Debugging information:
<br></br>tb_tar_exit_code: <code>$tb_tar_exit_code</code>
<br></br>pv_exit_code: <code>$pv_exit_code</code>
<br></br>tar_exit_code: <code>$tar_exit_code</code>
<br></br>pv_wrapper_exit_code: <code>$pv_wrapper_exit_code</code>)
<br></br>
<br></br>Please report this bug!</p>"
      output_gui ${output_opts[@]} --messagex --typex "error" --message "$MSG" --done

      output_cli error "$MSG"

      tb_exit_function 15
   fi

   log notice "Extraction of '$file_name' has been completed."
}

tb_patch_download_folder_create() {
   mkdir --parents -- "$tb_extract_temp_folder/Browser/Downloads"
}

tb_patch() {
   if [ "$TB_NO_PATCH" = "true" ]; then
      true "INFO: Skipping ${FUNCNAME[0]}, because TB_NO_PATCH is 'true', ok."
      return 0
   fi

   tb_run_function tb_patch_download_folder_create
}

tb_install() {
   if [ "$TB_NO_INSTALL" = "true" ]; then
      true "INFO: Skipping ${FUNCNAME[0]}, because TB_NO_INSTALL is 'true', ok."
      return 0
   fi

   log notice "Deleting old folder '$tb_browser_folder'."
   safe-rm --recursive --force -- "$tb_browser_folder"

   log notice "Moving temporary folder '$tb_extract_temp_folder' to '$tb_browser_folder'."
   mv -- "$tb_extract_temp_folder" "$tb_browser_folder"
}

tb_cleanup() {
   if [ "$TB_NO_INSTALL" = "true" ]; then
      true "INFO: Skipping ${FUNCNAME[0]}, because TB_NO_INSTALL is 'true', ok."
      return 0
   fi
   if [ "$TB_NO_CLEANUP" = "true" ]; then
      log notice "Skipping ${FUNCNAME[0]}, because TB_NO_CLEANUP is 'true', ok."
      return 0
   fi

   log notice "Deleting no longer required file '$TBB_PACKAGE_FULL_PATH' to save space."
   safe-rm --force -- "$TBB_PACKAGE_FULL_PATH"
}

tb_hardcoded_version_last_downloaded_save() {
   overwrite "$tbb_version_last_downloaded_save_file" "$tbb_version_stripped"
   log notice "Saved '$tbb_version_stripped' in '$tbb_version_last_downloaded_save_file'."
}

tb_start_ask() {
   if [ "$noaskstart" = "true" ]; then
      true "INFO: noaskstart is set to 'true'. Skipping question if '$tb_title' should be started."
      return 0
   fi

   if [ "$TB_FORCE_INSTALL" = "1" ]; then
      log notice "Finished installing '$tb_title'. Can be found in folder '$tb_browser_folder'."
      log notice "Not starting '$tb_title', because variable TB_FORCE_INSTALL is set to '1'."
      return 0
   fi

   local has_torbrowser_exit_code="0"
   has "$tb_bin" || { has_torbrowser_exit_code="$?" ; true; };

   if [ ! "$has_torbrowser_exit_code" = "0" ]; then
      log notice "'$tb_bin' binary not found in path. tb-starter is probably not installed. \
Skipping question to start '$tb_title'."
      return 0
   fi

   if [ "$noaskstart" = "false" ]; then
      true "noaskstart is 'false'"
   else
      if [ "$running_inside_qubes_template_vm" = "true" ] ; then
         MSG="<p>Finished installing '$tb_title'. Can be found in '$tb_browser_folder'.</p>

<p>Not asking to start $tb_title, because running in a Template.</p>

<p>To run '$tb_title', start an App Qube, Standalone or Disposable, then run:<br />
<code>$tb_bin</code></p>"
         output_gui ${output_opts[@]} --messagex --typex "info" --message "$MSG" --done

         output_cli notice "$MSG"

         return 0
      fi
   fi

   MSG="<p>Finished installing $tb_title. Can be found in $tb_browser_folder.</p>

<p>Please donate!
<br></br><a href=$tb_documentation_base_url_clearnet/wiki/Donate>$tb_documentation_base_url_clearnet/wiki/Donate</a></p>"
   local question="Start $tb_title?"
   local button="yesno"
   local answer

   if [ "$TB_INPUT" = "none" ]; then
      true "INFO: TB_INPUT is set to '$TB_INPUT'. Skipping question if $tb_title should be started."
      return 0
   fi

   if [ "$TB_INPUT" = "stdin" ]; then
      log question "QUESTION: $question
y/n?"
      read -r answer
      if [ ! "$answer" = "y" ]; then
         log notice "Canceled starting '$tb_title', ok."
      else
         $tb_bin
      fi
      return 0
   fi

   log notice "GUI is asking to start $tb_title."
   answer="$(/usr/libexec/msgcollector/generic_gui_message "info" "$TITLE" "$MSG" "$question" "$button")"
   if [ ! "$answer" = "16384" ]; then ## Button 'Yes' has not been pressed.
      log notice "Canceled starting '$tb_title', ok."
      return 0
   else
      log notice "Running '$tb_title'."
      $tb_bin
      log notice "'$tb_title' was terminated, ok."
      return 0
   fi
}

main_function() {
   tb_run_function root_check "$@"
   tb_run_function qubes_template_check "$@"

   tb_run_function sysmaint_check "$@"
   tb_run_function tb_sanity_tests
   tb_run_function tb_config_folder_parser
   tb_run_function tb_parse_cmd_options "$@"
   tb_run_function tb_preparation
   tb_run_function tb_stdin
   tb_run_function tb_qubes_dvm_template
   tb_run_function tb_create_folders
   tb_run_function tb_local_version_detection

   if [ "$tb_hard_reset" == "true" ]; then
      tb_run_function tb_kill_already_running_tb_maybe
      tb_run_function tb_version_processing
      tb_run_function tb_reset
      tb_run_function tb_gpg_verify
      tb_run_function tb_extract
      tb_run_function tb_patch
      tb_run_function tb_install
      tb_run_function tb_cleanup
      tb_run_function tb_start_ask
      return 0
   fi

   #tb_run_function tb_skip_if_higher_or_equal_version_already_downloaded
   tb_run_function tb_connectivity_checks_interfaces
   tb_run_function tb_connectivity_checks_tor
   tb_run_function tb_connectivity_checks_curl
   tb_run_function tb_update_check
   tb_run_function tb_remote_version_parser
   tb_run_function tb_remote_version_sanity_test

   tb_run_function tb_confirm_update
   tb_run_function tb_version_processing
   tb_run_function tb_kill_already_running_tb_maybe
   tb_run_function tb_download_files
   tb_run_function tb_gpg_verify
   tb_run_function tb_confirm_install
   tb_run_function tb_extract
   tb_run_function tb_patch
   tb_run_function tb_install
   tb_run_function tb_cleanup
   tb_run_function tb_hardcoded_version_last_downloaded_save
   tb_run_function tb_start_ask
}

if [ "$tb_updater_was_sourced" = "true" ]; then
  true "INFO: Not running because it was sourced by another script."
else
  true "INFO: Running because it was executed."
  tb_run_function main_function "$@"
fi
