#!/bin/bash

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

#set -x
#exec 1>/tmp/torbrowser.log
#exec 2>/tmp/torbrowser.log

set -e
set -o pipefail
set -o errtrace

if [ -f /usr/libexec/helper-scripts/pre.bsh ]; then
   source /usr/libexec/helper-scripts/pre.bsh
fi

## sets: boot_session
if [ -f /usr/libexec/helper-scripts/boot-session-detection.bsh ]; then
   source /usr/libexec/helper-scripts/boot-session-detection.bsh
fi

[ -n "$SCRIPTNAME" ] || SCRIPTNAME="$(basename "$BASH_SOURCE")"

[ -n "$IDENTIFIER" ] || IDENTIFIER="torbrowser"

[ -n "$ICON" ] || ICON="/usr/share/icons/icon-pack-dist/tbupdate.ico"

tb_error_handler() {
   local exit_code="$?"

   local MSG="$0 script bug.

No panic. Nothing is broken. Just some rare condition has been hit.
Try again later. If this is a transient issue, it can be safely ignored.

There is likely a solution for this problem.

* Please see the $tb_downloader_developers News. <a href=$tb_documentation_base_url_clearnet/wiki/Stay_Tuned>$tb_documentation_base_url_clearnet/wiki/Stay_Tuned</a>
* User Help Forum.
* And Documentation. <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki>$tb_documentation_base_url_clearnet/wiki/$tb_wiki</a>

It may or may not be possible to fix but further information may be required.

* Try start from command line. <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#From_the_Command_Line>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#From_the_Command_Line</a>
* Try start from command line in debug mode. <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#In_Debugging_Mode>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#In_Debugging_Mode</a>
* If you don't mind to repair that Tor Browser install, reinstall of Tor Browser would also likely solve this. <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#reinstall>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#reinstall</a>

Debugging information:

BASH_COMMAND: <code>$BASH_COMMAND</code>
exit_code: <code>$exit_code</code>

tb_browser_folder: <code>$tb_browser_folder</code>

tb_prefs_js_target_file: <code>$tb_prefs_js_target_file</code>
tb_user_js_target_file: <code>$tb_user_js_target_file</code>

output: <code>$output</code>
output_opts: <code>${output_opts[@]}</code>
progressbaridx: <code>$progressbaridx</code>

For debugging, run:
<code>bash -x $SCRIPTNAME</code>"

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

   if [ "$progressbaridx" = "" ]; then
      true
   else
      $output ${output_opts[@]} --progressbaridx "$progressbaridx" --progressx "100"
   fi
   $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
   $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
   exit 1
}

trap "tb_error_handler" ERR

tb_run_function() {
   case $tb_skip_functions in
   *"$@"*)
      true "INFO: Skipping $@, because tb_skip_functions includes it."
      return 0
      ;;
   esac

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

root_check() {
   if [ "$(id -u)" != "0" ]; then
      #echo "$SCRIPTNAME running as user."
      tb_running_as_root=false
   else
      tb_running_as_root=true
      echo "Do not run $SCRIPTNAME as root!"
      exit 1
   fi
}

tb_preparation() {
   who_ami="$(whoami)"
   if command -v qubesdb-read &>/dev/null ; then
      [ -n "$is_qubes" ] || is_qubes=true
      ## qubesdb-read fails inside chroot, therefore overwriting with '|| true'.
      [ -n "$qubes_vm_name" ] || qubes_vm_name="$(qubesdb-read /name)" || true
      [ -n "$qubes_vm_persistence" ] || qubes_vm_persistence="$(qubesdb-read /qubes-vm-persistence)" || true
   else
      [ -n "$is_qubes" ] || is_qubes=false
   fi

   if test -f /run/qubes/this-is-templatevm ; then
      if [ "$tb_user_home" = "" ]; then
         tb_user_home="/var/cache/tb-binary"
      fi
   fi

   [ -n "$tb_user_home" ] || tb_user_home="$HOME"
   ## example tb_user_home:
   ## /home/user
   ## When double clicking a file downloaded in Tor Browser download tab
   ## '$HOME' would be set to '/home/user/.tb/tor-browser/Browser' which would lead
   ## to a false-positive "Tor Browser not installed" message.
   if printf '%s\n' "$tb_user_home" | grep -- tor-browser >/dev/null 2>/dev/null; then
      tb_user_home="/home/$who_ami"
   fi

   tb_user_home_dirname="$(dirname "$tb_user_home")"

   [ -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_settings_folder" ] || tb_settings_folder="torbrowser.d"
   [ -n "$tb_downloader_command" ] || tb_downloader_command="update-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_title" ] || tb_title="Tor Browser"
   [ -n "$tb_wiki" ] || tb_wiki="Tor_Browser"
   [ -n "$tb_bin" ] || tb_bin="torbrowser"
   [ -n "$tb_browser_runner" ] || tb_browser_runner="start-tor-browser"
   [ -n "$IDENTIFIER" ] || IDENTIFIER="$tb_bin"

   [ -n "$tb_home_folder" ] || tb_home_folder="$tb_user_home/$tb_install_folder_dot"
   ## example tb_home_folder:
   ## /home/user/.tb
   [ -n "$tb_browser_folder" ] || tb_browser_folder="$tb_home_folder/$tb_browser_name"
   ## example tb_browser_folder:
   ## /home/user/.tb/tor-browser

   [ -n "$tb_prefs_js_target_file" ] || tb_prefs_js_target_file="$tb_browser_folder/Browser/TorBrowser/Data/Browser/profile.default/prefs.js"
   [ -n "$tb_user_js_target_file" ] || tb_user_js_target_file="$tb_browser_folder/Browser/TorBrowser/Data/Browser/profile.default/user.js"

   [ -n "$TOR_DEFAULT_HOMEPAGE" ] || TOR_DEFAULT_HOMEPAGE="about:blank"
   export TOR_DEFAULT_HOMEPAGE

   output="/usr/libexec/msgcollector/msgcollector"

   local my_tty
   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

   who_ami="$(whoami)"

   output_opt_1="--icon $ICON"
   output_opt_2="--parentpid $$"
   output_opt_3="--identifier ${IDENTIFIER}"
   output_opt_4="--parenttty $my_tty"

   output_opts=( "$output_opt_1" "$output_opt_2" "$output_opt_3" "$output_opt_4" "$output_opt_5")

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

   ## TODO
   #$output ${output_opts[@]} --forget
}

tb_config_folder_parser() {
   [ -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

   local j
   for j in \
      /etc/${tb_settings_folder}/user.js \
      /usr/local/etc/${tb_settings_folder}/user.js \
      ; do
         if ! test -r "$j" ; then
            continue
         fi
         [ -n "$tb_user_js_source_file" ] || tb_user_js_source_file="$j"
   done
}

tb_templatevm_check() {
   if [ "$is_qubes" = "false" ]; then
      true "Not running in Qubes."
      return 0
   fi

   if ! test -f /run/qubes/this-is-templatevm ; then
      true "Not running in Template."
      return 0
   fi

   if [ "$tb_allow_start_in_templatevm" = "true" ]; then
      true "tb_allow_start_in_templatevm is true."
      return 0
   fi

   tb_qubes_wiki="in_qubes-whonix"
   local MSG="<p>Do not run $tb_title in Template.<br></br>
<br></br>
More info: <a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#$tb_qubes_wiki>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#$tb_qubes_wiki</a></p>"
   $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
   $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done

   exit 3
}

tb_qubes_dvm_template() {
   ## Avoid running in Qubes DVM Template.
   ## https://phabricator.whonix.org/T726
   if printf '%s\n' "$qubes_vm_name" | grep --invert-match -- "-dvm" >/dev/null 2>/dev/null; then
      true "INFO: not running inside Qubes DVM Template, ok."
      return 0
   fi

   local MSG="\
<p>Do not run $tb_title in Qubes DVM 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 ${output_opts[@]} --messagex --titlex "$TITLE" --typex "error" --message "$MSG" --done
   $output ${output_opts[@]} --messagecli --titlecli "$TITLE" --typecli "error" --message "$MSG" --done
   exit 1
}

remount_exec() {
   if [ "$qubes_vm_persistence" = "none" ]; then
      true "$FUNCNAME: Not remounting exec in DispVM."
      return 0
   fi

   if check_noexec_do ; then
      true "INFO: tb_home_folder '$tb_home_folder' is not mounted with noexec."
      return 0
   fi

   leaprun tb-starter-remount-exec
}

check_noexec_do() {
   ## example tb_home_folder:
   ## /home/user/.tb
   tb_home_folder_mount="$(mount | grep -- "$tb_home_folder")" || true
   if [ ! "$tb_home_folder_mount" = "" ]; then
      if printf '%s\n' "$tb_home_folder" | grep --invert-match -- "noexec" >/dev/null 2>/dev/null ; then
         ## 'noexec' not found inside 'mount' line for tb_home_folder.
         ## I.e. not mounted noexec.
         true "INFO: noexec detected: no"
         return 1
      fi
   fi
   true "INFO: noexec detected: yes"
   return 0
}

check_noexec() {
   if check_noexec_do ; then
      true "INFO: tb_home_folder '$tb_home_folder' is not mounted with noexec."
      return 0
   fi

   ## example tb_user_home:
   ## /home/user
   ##
   ## example tb_user_home:
   ## /var/cache/tb-binary
   ##
   ## example tb_user_home_dirname:
   ## /var/cache
   ##
   ## example tb_user_home_dirname:
   ## /home
   if mount | grep -- "$tb_user_home_dirname" | grep -- "noexec" >/dev/null 2>/dev/null; then
      local MSG="<p>Failed to start $tb_title!<br></br>
<br></br>
<code>$tb_user_home_dirname</code> is mounted with <code>noexec</code>.
<br></br>
<br></br>Debugging information:
<br></br>
<br></br>tb_user_home_dirname: <code>$tb_user_home_dirname</code>
<br></br>tb_home_folder_mount: <code>$tb_home_folder_mount</code>
</p>"
      $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
      $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
      exit 5
   fi
}

check_tb_updater_first_boot_done() {
   if test -f /run/qubes/this-is-templatevm ; then
      true "$FUNCNAME: Detected TemplateVM. Skipping tb-updater-first-boot.service check."
      return 0
   fi

   ## Waiting for tb-updater-first-boot.service is useful in Qubes to make sure
   ## permission fix is done for Qubes DispVMs.

   local systemctl_output
   local wait_counter
   wait_counter="0"
   while true ; do
      if systemctl_output="$(systemctl --no-pager --no-block status tb-updater-first-boot.service 2>&1)" ; then
         break
      fi
      wait_counter="$(( wait_counter + 1 ))"
      sleep 1 &
      wait "$!"
      if [ "$wait_counter" -ge 20 ]; then
         systemctl_output="$(/usr/libexec/msgcollector/br_add "$systemctl_output")"
         local MSG="<p>Unable to start <code>$tb_title</code>.<br>
<br>
Debugging information:
<br>
Failed to run:<br>
<br><code>systemctl --no-pager --no-block status tb-updater-first-boot.service</code>.<br>
<br>
systemctl output:<br>
<br><code>$systemctl_output</code><br>
<br>
To see this for yourself, you could try: <blockquote>Start Menu -> System -> QTerminal<br>
Then run:<br>
<code>systemctl --no-pager --no-block status tb-updater-first-boot.service</code></blockquote></p>"
         $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
         $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
         exit 4
      fi
   done
}

tb_permission_issue() {
   local failed_command="$@"
   local MSG="<p>User home folder permission issue?
<br></br>
<br></br>Failed to run the following command:
<br></br>
<br></br><code>$failed_command</code>
<br></br>
<br></br>Tip: If something does not work, do not arbitrarily try to use sudo / root without indication that this would be appropriate.
<br></br>That only risks messing up user home folder permissions. See:
<br></br><a href=https://www.kicksecure.com/wiki/Root#Inappropriate_Use_of_Root_Rights>https://www.kicksecure.com/wiki/Root#Inappropriate_Use_of_Root_Rights</a>
<br></br>
<br></br>To fix this, you could try: <blockquote>Start Menu -> System -> QTerminal
<br></br>
<br></br><code>sudo chown --recursive \"$who_ami\":\"$who_ami\" \"$tb_user_home\"</code></blockquote>
<br></br>Then try starting $tb_title again.
<br></br>
<br></br>To debug this, you could try to start $tb_title Starter (by $tb_downloader_developers developers) from command line in:
<br></br><a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#In_Verbose_Mode>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#In_Verbose_Mode</a>.
<br></br>
<br></br>See also $tb_title Starter (by $tb_downloader_developers developers) Permission Issues:
<br></br><a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Permission_Issues>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Permission_Issues</a></p>"

   echo "DEBUG START:"

   echo ls -la "$tb_user_home" || true
   ls -la "$tb_user_home" || true

   echo ls -la "$tb_user_home/.cache" || true
   ls -la "$tb_user_home/.cache" || true

   echo ls -la "$tb_user_home/.cache/tb" || true
   ls -la "$tb_user_home/.cache/tb" || true

   echo ls -la "$tb_user_home/.cache/tb/temp" || true
   ls -la "$tb_user_home/.cache/tb/temp" || true

   echo ls -la "$tb_user_home/.cache/tb/files" || true
   ls -la "$tb_user_home/.cache/tb/files" || true

   echo ls -la "$tb_user_home/.cache/tb/tbb_version_last_downloaded_save_file" || true
   ls -la "$tb_user_home/.cache/tb/tbb_version_last_downloaded_save_file" || true

   echo "DEBUG END"

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

tb_copy_from_root_to_user_maybe() {
   if [ "$qubes_vm_persistence" = "none" ]; then
      if [ "$started_by_sandbox_app_launcher" = "true" ] || [ "$tb_installer_started_by_sandbox_app_launcher" = "true" ]; then
         true "$FUNCNAME: Considering copying from /var/cache/tb-binary to tb_user_home $tb_user_home in DispVM since started_by_sandbox_app_launcher=true."
         true "$FUNCNAME: (Mounting /var/cache/tb-binary/.cache/.tb to /home/sandbox-app-launcher-appdata/torbrowser/.tb/ in DispVM is not yet implemented.)"
      else
         true "$FUNCNAME: Not copying from /var/cache/tb-binary to tb_user_home $tb_user_home in DispVM."
         true "$FUNCNAME: Handing this is responsibility of tb-updater / tb-updater-dispvm.service / /usr/lib/tb-updater/dispvm."
         return 0
      fi
   fi

   local done_file
   ## example tb_home_folder:
   ## /home/user/.tb
   done_file="$tb_home_folder/first-boot-home-population.done"
   ## example done_file:
   ## /home/user/.tb/first-boot-home-population.done

   ## example tb_user_home:
   ## /home/user
   if [ ! -d "$tb_user_home" ]; then
      return 0
   fi

   if [ ! -d /var/cache/tb-binary ]; then
      return 0
   fi

   if [ -d "$tb_browser_folder" ]; then
      return 0
   fi

   if [ -e "$done_file" ]; then
      return 0
   fi

   if [ -d /var/cache/tb-binary/.cache ]; then
       if ! mkdir -p "$tb_user_home/.cache"  ; then
         tb_permission_issue mkdir -p "$tb_user_home/.cache"
      fi
      if ! test -w "$tb_user_home/.cache" ; then
         tb_permission_issue test -w "$tb_user_home/.cache"
      fi
      if ! cp --verbose --recursive --no-clobber "/var/cache/tb-binary/.cache" "$tb_user_home/" ; then
         tb_permission_issue cp --verbose --recursive --no-clobber "/var/cache/tb-binary/.cache" "$tb_user_home/"
      fi
   fi

   ## example tb_home_folder:
   ## /home/user/.tb
   ## example tb_browser_folder:
   ## /home/user/.tb/tor-browser

   if [ "$tb_i2p_browser" = "true" ]; then
      if [ -d "/var/cache/tb-binary/.tb/tor-browser" ]; then
         if ! mkdir -p "$tb_home_folder" ; then
            tb_permission_issue mkdir -p "$tb_home_folder"
         fi
         if ! test -w "$tb_home_folder" ; then
            tb_permission_issue test -w "$tb_home_folder"
         fi
         ## No / at the end.
         ## example simplified:
         ## cp /var/cache/tb-binary/.tb/tor-browser /home/user/.i2pb/i2p-browser
         if ! cp --verbose --recursive --no-clobber "/var/cache/tb-binary/.tb/tor-browser" "$tb_browser_folder" ; then
            tb_permission_issue cp --verbose --recursive --no-clobber "/var/cache/tb-binary/.tb/tor-browser" "$tb_browser_folder"
         fi
         touch "$done_file"
      fi
   else
      if [ -d "/var/cache/tb-binary/$tb_install_folder_dot/$tb_browser_name" ]; then
         if ! mkdir -p "$tb_home_folder" ; then
            tb_permission_issue mkdir -p "$tb_home_folder"
         fi
         if ! test -w "$tb_home_folder" ; then
            tb_permission_issue test -w "$tb_home_folder"
         fi

         ## No / at the end.
         ## example simplified:
         ## cp /var/cache/tb-binary/.tb/tor-browser /home/user/.tb/tor-browser
         if ! cp --verbose --recursive --no-clobber "/var/cache/tb-binary/$tb_install_folder_dot/$tb_browser_name" "$tb_browser_folder" ; then
             tb_permission_issue cp --verbose --recursive --no-clobber "/var/cache/tb-binary/$tb_install_folder_dot/$tb_browser_name" "$tb_browser_folder"
         fi

         touch "$done_file"
      fi
   fi
}

maybe_install_tor_browser() {
   if [ -d "$tb_browser_folder" ]; then
      return 0
   fi

   local MSG="<p>$tb_title is currently not installed.
<br></br>(Folder $tb_browser_folder does not exist.)</p>"
   local question="Start $tb_title Downloader (by $tb_downloader_developers developers)?"
   local button="yesno"
   local answer
   answer="$(/usr/libexec/msgcollector/generic_gui_message "error" "$TITLE" "$MSG" "$question" "$button")"
   #answer="0"
   #yad --title="$TITLE" --question --text "$MSG" || { answer="$?" ; true; };
   ## yad exit codes
   ## no 1
   ## yes 0
   if [ "$answer" = "16384" ]; then ## button 'yes' pressed
      local update_torbrowser_command_v_exit_code="0"
      command -v "$tb_downloader_command" &>/dev/null || { update_torbrowser_command_v_exit_code="$?" ; true; };
      if [ "$update_torbrowser_command_v_exit_code" = "0" ]; then
         ## update-torbrowser / update-mullvadbrowser is available.
         ## || true, in case update-torbrowser / update-mullvadbrowser fails for some reason (no
         ## internet connection).
         "$tb_downloader_command" $tb_using_i2p --noaskstart || true
      else
         ## update-torbrowser / update-mullvadbrowser is not available.
         MSG="<p>$tb_title Updater (by $tb_downloader_developers developers) is not installed.
$tb_downloader_command $tb_using_i2p is not available. Please install the tb-updater package.
<br></br>Run:
<blockquote>sudo apt-get install tb-updater</blockquote>
Then try again.</p>"
         $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
         $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
      fi
   else  ## button 'yes' not pressed
      exit 0
   fi

   if [ ! -d "$tb_browser_folder" ]; then
      ## Still not installed.
      exit 0
   fi
}

tb_folder_permission_check() {
   if ! test -w "$tb_browser_folder" ; then
      tb_permission_issue test -w "$tb_browser_folder"
   fi
}

tb_folder_change_directory() {
   if ! cd "$tb_browser_folder" ; then
      tb_permission_issue cd "$tb_browser_folder"
   fi
}

tb_detect_starter_bin() {
   if [ ! "$tb_starter_bin" = "" ]; then
      return 0
   fi

   if [ -x "$tb_browser_folder/Browser/$tb_browser_runner" ]; then
      ## Alternative way to start Tor Browser.
      ## https://github.com/netblue30/firejail/issues/2863#issuecomment-513219822
      ## working:
      ## firejail /home/user/.tb/tor-browser/Browser/start-tor-browser
      ## not working:
      ## firejail /home/user/.tb/tor-browser/start-tor-browser.desktop
      tb_starter_bin="$tb_browser_folder/Browser/$tb_browser_runner"
   elif [ -x "$tb_browser_folder/$tb_browser_runner" ]; then
      ## Previously used filename no longer existing in current version of
      ## Tor Browser. Keeping it in case it $tb_browser_runner.desktop gets
      ## abolished and this one revived.
      tb_starter_bin="$tb_browser_folder/$tb_browser_runner"
   elif [ -x "$tb_browser_folder/$tb_browser_runner.desktop" ]; then
      ## Canonical way to start Tor Browser.
      ## Cannot be used in Qubes Disposables.
      ## ~/.tb/tor-browser/start-tor-browser.desktop does as per Tor Browser upstream default:
      ## Exec=sh -c '"$(dirname "$*")"/Browser/start-tor-browser --detach || ([ ! -x "$(dirname "$*")"/Browser/start-tor-browser ] && "$(dirname "$*")"/start-tor-browser --detach)' dummy %k X-TorBrowser-ExecShell=./Browser/start-tor-browser --detach
      ## Qubes start menu incompatible with DispVMs launching GUI applications into the background #5129
      ## https://github.com/QubesOS/qubes-issues/issues/5129
      tb_starter_bin="$tb_browser_folder/$tb_browser_runner.desktop"
   else
      local MSG="Neither <code>$tb_browser_folder/Browser/$tb_browser_runner</code> nor\
<code>$tb_browser_folder/$tb_browser_runner</code> nor \
<code>$tb_browser_folder/$tb_browser_runner.desktop</code> is executable."
      $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
      $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
      exit 2
   fi
}

tb_custom_user_js_file() {
   if ! test -f "$tb_user_js_source_file" ; then
      true "tb_user_js_source_file '$tb_user_js_source_file' does not exist. Skipping to copy, ok."
      return 0
   fi
   cp "$tb_user_js_source_file" "$tb_user_js_target_file"
}

tb_prefs_js_file_patches() {
   local search replace file_name
   ## Path to local browser homepage changed in during Whonix 15 to Whonix 16 upgrade.
   search="/usr/share/homepage/whonix-welcome-page/whonix.html"
   replace="/usr/share/doc/homepage/whonix-welcome-page/whonix.html"
   file_name="$tb_prefs_js_target_file"
   if ! test -f "$file_name" ; then
      return 0
   fi
   str_replace "$search" "$replace" "$file_name" &>/dev/null || true
}

tb_i2p() {
   ## i2pbrowser sets tb_i2p_browser="true".

   ## If the i2pmarker file exists and if tb_i2p_browser is not set to
   ## "true" it means that this browser was previously started with
   ## tb_i2p_browser="true". Therefore refuse to start it without
   ## tb_i2p_browser="true" since a browser that was ever used with i2p
   ## should never be used again over Tor to avoid linking the different
   ## identities.
   if test -f "$tb_browser_folder/i2pmarker" ; then
      if [ ! "$tb_i2p_browser" = "true" ]; then
         local MSG="i2p marker file exists and trying to start without i2pbrowser. Aborted. A Tor Browser that was previously started as i2pbrowser should not be started without i2pbrowser."
         $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
         $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
         exit 2
         return 0
      fi
   fi

   if [ ! "$tb_i2p_browser" = "true" ]; then
      ## Since tb_i2p_browser is not set to true,
      ## do not proceed with the rest of this function, i.e.
      ## creating i2pmarker or making i2pbrowser modifications.
      return 0
   fi

   ## Check if i2p related services are already running.
   if ! /usr/share/tb-profile-i2p/check-i2p ; then
      ## i2p related services are already not yet running.

      ## prompt, systemctl enable and start i2p related services.
      if ! pkexec /usr/share/tb-profile-i2p/enable-i2p; then
         local MSG="\
<p>Cannot enable i2p service!<br></br>
<br></br>
Most likely user-sysmaint-split is installed and you are booted into 'PERSISTENT Mode - USER Session' or 'LIVE Mode - USER Session'. To enable i2p, reboot and select 'PERSISTENT Mode - SYSMAINT Session', then open a terminal and run 'pkexec /usr/share/tb-profile-i2p/enable-i2p'. More info: <a href=https://www.kicksecure.com/wiki/Sysmaint>https://www.kicksecure.com/wiki/Sysmaint</a>"

         $output ${output_opts[@]} --messagex --titlex "$TITLE" --typex "error" --message "$MSG" --done
         $output ${output_opts[@]} --messagecli --titlecli "$TITLE" --typecli "error" --message "$MSG" --done
         exit 1
      fi
   fi
   ## This effectively results in a one time pkexec prompt for users of
   ## i2pbrowser in Non-Qubes-Whonix.
   ##
   ## This is different in Qubes-Whonix App Qubes due to
   ## App Qubes having a non-persistent root image by default.
   ## These users should run:
   ## sudo /usr/share/tb-profile-i2p/enable-i2p
   ## in Whonix-Workstation Template.

   if ! test -f "$tb_browser_folder/i2pmarker" ; then
      touch "$tb_browser_folder/i2pmarker"
   fi

   cp -r --no-clobber /usr/share/tb-profile-i2p/profile.i2p \
      "$tb_browser_folder/profile.i2p"

   rm -rf "$tb_browser_folder/profile.i2p/extensions"

   cp -r --no-clobber "$tb_browser_folder/Browser/TorBrowser/Data/Browser/profile.default/extensions" \
      "$tb_browser_folder/profile.i2p/extensions"

   if [ ! -L "$tb_browser_folder/Browser/TorBrowser/Data/Browser/profile.i2p" ]; then
      ln -sf "$tb_browser_folder/profile.i2p" \
         "$tb_browser_folder/Browser/TorBrowser/Data/Browser/profile.i2p"
   fi

   cp -f /usr/share/tb-profile-i2p/start-i2p-browser "$tb_browser_folder/Browser/start-i2p-browser"
}

maybe_use_open_link_confirmation() {
   ## Environment variable OPEN_LINK_CONFIRMATION might be set to 'true' if
   ## we are run by open_link_confirmation.

   if [ "$open_link_confirmation_skip" = "true" ]; then
      return 0
   fi

   if ! test -x /usr/libexec/open-link-confirmation/open-link-confirmation ; then
      return 0
   fi

   if [ "$OPEN_LINK_CONFIRMATION" = "true" ]; then
      ## We are run by open_link_confirmation.
      ## Not run open-link-confirmation again.
      ## Open browser.
      return 0
   fi

   if [[ "$@" = "" ]]; then
      ## No args set so also no link set. No need to ask.
      return 0
   fi

   tb_starter_tool="/usr/libexec/open-link-confirmation/open-link-confirmation"
   tb_start_command_temp="$tb_starter_tool $@"

   tb_exit_code="0"
   $tb_starter_tool "$@" || { tb_exit_code="$?" ; true; };

   tb_used_open_link_confirmation=true

   tb_run_function tb_handle_exit_code
}

tb_firejail_set_options() {
   [ -n "$tb_firejail_bin" ] || tb_firejail_bin="firejail"

   ## https://github.com/netblue30/firejail/issues/2863
   tb_firejailopts[${#tb_firejailopts[@]}]="--whitelist=$tb_browser_folder"

   ## /etc/resolv.conf is a symlink to /etc/resolv.conf.kicksecure in Kicksecure.
   ## https://github.com/netblue30/firejail/issues/2966
   tb_firejailopts[${#tb_firejailopts[@]}]="--private-etc=resolv.conf.kicksecure"

   tb_firejailopts[${#tb_firejailopts[@]}]="--profile=torbrowser-launcher"

   ## https://forums.whonix.org/t/automatically-firejailing-tor-browser/4767/21
   #tb_firejailopts[${#tb_firejailopts[@]}]="--x11=xorg"

   tb_firejailopts[${#tb_firejailopts[@]}]="--quiet"
}

tb_start_tor_browser() {
   tb_exit_code="0"
   if [ "$tb_hardening" = "true" ]; then
      tb_start_command_temp="$tb_firejail_bin ${tb_firejailopts[@]} $tb_starter_bin_pre $tb_starter_bin --verbose --allow-remote $tb_starter_bin_post $@"
      $tb_firejail_bin ${tb_firejailopts[@]} $tb_starter_bin_pre $tb_starter_bin --verbose --allow-remote $tb_starter_bin_post "$@" || { tb_exit_code="$?" ; true; };
   else
      tb_start_command_temp="$tb_starter_bin_pre $tb_starter_bin --verbose --allow-remote $tb_starter_bin_post $@"
      $tb_starter_bin_pre $tb_starter_bin --verbose --allow-remote $tb_starter_bin_post "$@" || { tb_exit_code="$?" ; true; };
   fi

   tb_run_function tb_handle_exit_code
}

tb_handle_exit_code() {
   [ -n "$OPEN_LINK_CONFIRMATION_COUNTER" ] || OPEN_LINK_CONFIRMATION_COUNTER="0"

   if [ ! "$tb_exit_code" = "0" ]; then
      local MSG="<p>$tb_title ended with non-zero (error) exit code!<br></br>
<br></br>
$tb_title was started with:<br></br>
<br></br><code>$tb_start_command_temp</code>
<br></br>
<br></br>$tb_title exited with code: <code>$tb_exit_code</code>
<br></br>OPEN_LINK_CONFIRMATION_COUNTER: <code>$OPEN_LINK_CONFIRMATION_COUNTER</code>
<br></br>
<br></br>To see this for yourself, you could try: <blockquote>Start Menu -> System -> QTerminal
<br></br>Then run:
<br></br><code>$tb_start_command_temp</code></blockquote>

See online documentation:
<br></br><a href=$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Crash_Errors>$tb_documentation_base_url_clearnet/wiki/$tb_wiki#Crash_Errors</a></p>"
      if [ "$tb_used_open_link_confirmation" = "true" ]; then
         true "INFO: Avoid duplicate error popup."
      else
         $output ${output_opts[@]} --messagex --typex "error" --titlex "$TITLE" --message "$MSG" --done
         $output ${output_opts[@]} --messagecli --typecli "error" --titlecli "$TITLE" --message "$MSG" --done
      fi
      exit "$tb_exit_code"
   fi
   exit 0
}

tb_check_allowed(){
   if [ "$tb_no_start" = "true" ]; then
      true "INFO: not starting $tb_title as tb_no_start is set to true."
      local MSG="\
<p>Not starting $tb_title as variable \$tb_no_start is set to \"true\"!<br></br>
<br></br>
More info: User configured tb_no_start=\"true\"</p>"

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

tb_sysmaint_check(){
   if [ "${tb_allow_start_in_sysmaint}" = 'true' ]; then
      true "tb_allow_start_in_sysmaint is true."
      return 0
   fi

   if [ "${boot_session}" = 'sysmaint_session' ]; then
      true "INFO: not starting $tb_title because running in sysmaint session and tb_allow_start_in_sysmaint is not set to true."
      local MSG="\
<p>Not starting $tb_title because running in sysmaint session and variable \$tb_allow_start_in_sysmaint is not set to \"true\"!<br>
<br>
Recommendation: boot into USER session instead.<br>
<br>
See also:<br>
https://www.whonix.org/wiki/Tor_Browser/Advanced_Users#sysmaint</p>"

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

main_function() {
   ## tb_error_handler
   tb_run_function root_check "$@"
   tb_run_function tb_preparation "$@"
   tb_run_function tb_config_folder_parser "$@"
   tb_run_function tb_check_allowed
   tb_run_function tb_sysmaint_check
   tb_run_function tb_templatevm_check "$@"
   tb_run_function tb_qubes_dvm_template "$@"
   tb_run_function check_tb_updater_first_boot_done "$@"
   tb_run_function tb_copy_from_root_to_user_maybe "$@"
   tb_run_function maybe_install_tor_browser "$@"
   tb_run_function remount_exec "$@"
   tb_run_function check_noexec "$@"
   tb_run_function tb_folder_permission_check "$@"
   tb_run_function tb_folder_change_directory "$@"
   tb_run_function tb_custom_user_js_file "$@"
   tb_run_function tb_prefs_js_file_patches "$@"
   tb_run_function tb_i2p "$@"
   tb_run_function tb_detect_starter_bin "$@"
   tb_run_function maybe_use_open_link_confirmation "$@"
   tb_run_function tb_firejail_set_options "$@"
   tb_run_function tb_start_tor_browser "$@"
}

tb_run_function main_function "$@"
