#!/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 "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/check_runtime.bsh

if was_executed "${BASH_SOURCE[0]}"; then
   #set -x
   set -o errexit
   set -o nounset
   set -o pipefail
   set -o errtrace
   shopt -s inherit_errexit
   shopt -s shift_verbose
   export LC_ALL=C

   true "$0: START"
fi

scriptname="$(basename -- "${BASH_SOURCE[0]}")"

timeout_command=("timeout" "--kill-after" "1" "2")

done=""
forceactive=""
forget=""
forgetwaitcli=""
icon=""
identifier=""
message=""
messagecli=""
messagex=""
newlinecli=""
nonewlinex=""
onlyecho=""
parentpid=""
parenttty=""
passivepopupqueuex=""
passivepopupqueuextitle=""
progressbaridx=""
progressbartitlex=""
progressbarx=""
progressbarxprogresstxtexisting=""
progressbarxrunning=""
progressx=""
status=""
titlecli=""
titlex=""
typecli=""
typecli_chosen=""
typex=""
typex_chosen=""
verbose=""
waitmessagecli=""
my_command_line_parameters=""
write_to_file=""

error_handler() {
   local exit_code="$?"

   local my_pstree
   my_pstree="$(pstree -p $$)" || true
   local ps_p_parentpid=""

   if [ ! "${parentpid}" = "" ]; then
      if [ ! "${parentpid}" = "0000000000" ]; then
         ps_p_parentpid="$(ps -p "${parentpid}")" || true
      fi
   fi

   local msg="\
###############################################################################
## ${scriptname} script bug.
## No panic. Nothing is broken. Just some rare condition has been hit.
## Try again later. There is likely a solution for this problem.
## Please see Whonix News, Whonix Blog and Whonix User Help Forum.
## Please report this bug!
##
## scriptname: '${scriptname}'
## identifier: '${identifier}'
## my_command_line_parameters: '${my_command_line_parameters}'
## write_to_file: '${write_to_file}'
## parentpid: '${parentpid}'
## ps_p_parentpid: '${ps_p_parentpid}'
## my_pstree: '${my_pstree}'
## BASH_COMMAND: '${BASH_COMMAND}'
## exit_code: '${exit_code}'
###############################################################################\
"
   printf '%s\n' "${msg}" >&2

   local home_var
   home_var=~/

   if ! test -w ~/ ; then
      printf '%s\n' "${scriptname}: skipping log to ~/.msgcollector/msgdispatcher-error.log since not writeable home_var: ${home_var}" >&2
      exit 1
   fi

   if [ ! -d ~/".msgcollector" ]; then
      mkdir --parents ~/".msgcollector"
   fi
   ## Make sure permissions (including the execute bit, needed to access a dir)
   ## are set properly
   "${timeout_command[@]}" chmod u+rwx -- ~/".msgcollector" || true

   if ! touch -- ~/".msgcollector/msgdispatcher-error.log" ; then
      printf '%s\n' "${scriptname}: skipping log to ${home_var}/.msgcollector/msgdispatcher-error.log since failed to 'touch' file." >&2
      exit 1
   fi

   if [ ! -w  ~/".msgcollector/msgdispatcher-error.log" ]; then
      printf '%s\n' "${scriptname}: skipping log to ~/.msgcollector/msgdispatcher-error.log since not writeable file: ${home_var}/.msgcollector" >&2
      exit 1
   fi

   "${timeout_command[@]}" append ~/".msgcollector/msgdispatcher-error.log" "\
scriptname: ${scriptname}
identifier: ${identifier}
my_command_line_parameters: ${my_command_line_parameters}
parentpid: ${parentpid}
ps_p_parentpid: ${ps_p_parentpid}
my_pstree: ${my_pstree}
BASH_COMMAND: ${BASH_COMMAND}
exit_code: ${exit_code}
" >/dev/null || true

   true "INFO: end of $0 error_handler, ok."

   exit 1
}

require_option_value() {
   if [ "${2}" -lt "2" ]; then
      printf '%s\n' "${BASH_SOURCE[0]}: ERROR: option ${1} requires a value."
      exit 1
   fi
}

parse_cmd_options() {
   trap "error_handler" ERR

   ## Thanks to:
   ## http://mywiki.wooledge.org/BashFAQ/035

   true "${cyan}INFO:${reset} parse_cmd_options msgcollector"

   while true; do
       case "${1:-}" in
           --verbose)
               set -x
               verbose="1"
               shift
               ;;
           --identifier)
               require_option_value "--identifier" "$#"
               identifier="${2:-}"
               shift 2
               ;;
           --icon)
               require_option_value "--icon" "$#"
               icon="${2:-}"
               if [ "${icon}" = "" ]; then
                  printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable icon is empty."
                  exit 1
               fi
               shift 2
               ;;
           --parentpid)
               require_option_value "--parentpid" "$#"
               parentpid="${2:-}"
               shift 2
               ;;
           --typecli)
               require_option_value "--typecli" "$#"
               typecli="${2:-}"
               shift 2
               if [ "${typecli}" = "" ]; then
                  printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable typecli is empty."
                  exit 1
               fi
               ;;
           --typex)
               require_option_value "--typex" "$#"
               typex="${2:-}"
               shift 2
               if [ "${typex}" = "" ]; then
                  printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable typex is empty."
                  exit 1
               fi
               if ! [[ "${typex}" =~ ^(info|warning|error)$ ]]; then
                  printf '%s\n' "${BASH_SOURCE[0]}: ERROR: invalid typex '${typex}' (expected info, warning, or error)."
                  exit 1
               fi
               ;;
           --typexstatus)
               ## for --status
               shift
               typex_chosen="1"
               ;;
           --typeclistatus)
               ## for --status
               shift
               typecli_chosen="1"
               ;;
           --message)
               require_option_value "--message" "$#"
               message="${2:-}"
               shift 2
               if [ "${message}" = "" ]; then
                  printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable message is empty."
                  exit 1
               fi
               ;;
           --titlecli)
               require_option_value "--titlecli" "$#"
               titlecli="${2:-}"
               shift 2
               ;;
           --titlex)
               require_option_value "--titlex" "$#"
               titlex="${2:-}"
               shift 2
               ;;
           --progressbartitlex)
               require_option_value "--progressbartitlex" "$#"
               progressbartitlex="${2:-}"
               shift 2
               ;;
           --passivepopupqueuextitle)
               require_option_value "--passivepopupqueuextitle" "$#"
               passivepopupqueuextitle="${2:-}"
               shift 2
               ;;
           --messagex)
               messagex="1"
               shift
               ;;
           --messagecli)
               messagecli="1"
               shift
               ;;
           --waitmessagecli)
               waitmessagecli="1"
               shift
               ;;
           --passivepopupqueuex)
               passivepopupqueuex="1"
               shift
               ;;
           --progressx)
               require_option_value "--progressx" "$#"
               progressx="${2:-}"
               shift 2
               if [ "${progressx}" = "" ]; then
                  printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable progressx is empty."
                  exit 1
               fi
               ;;
           --progressbarx)
               progressbarx="1"
               shift
               ;;
           --progressbarxrunning)
               progressbarxrunning="1"
               shift
               ;;
           --progressbarxprogresstxtexisting)
               progressbarxprogresstxtexisting="1"
               shift
               ;;
           --nonewlinex)
               nonewlinex="1"
               shift
               ;;
           --newlinecli)
               newlinecli="1"
               shift
               ;;
           --forceactive)
               forceactive="1"
               shift
               ;;
           --parenttty)
               require_option_value "--parenttty" "$#"
               parenttty="${2:-}"
               shift 2
               ;;
           --done)
               done="1"
               shift
               ;;
           --forget)
               forget="1"
               shift
               ;;
           --forgetwaitcli)
               forgetwaitcli="1"
               shift
               ;;
           --progressbaridx)
               require_option_value "--progressbaridx" "$#"
               progressbaridx="${2:-}"
               shift 2
               ;;
           --status)
               status="1"
               shift
               ;;
           --onlyecho)
               onlyecho="1"
               shift
               ;;
           --)
               shift
               break
               ;;
           -*)
               ## $1 is potentially attacker-controlled.
               sanitize-echo -- "${scriptname}: unknown option: $1" >&2
               exit 1
               ;;
           *)
               break
               ;;
       esac
   done

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

   if (( $# != 0 )); then
      sanitize-echo -- "${scriptname}: unknown option: $1" >&2
      exit 3
   fi
}

preparation() {
   trap "error_handler" ERR
}

colors() {
   trap "error_handler" ERR

   if [ ! "${TERM:-}" = "" ]; then
      COLOR_FORCE_YES=true
   fi
   # shellcheck source=../../../../helper-scripts/usr/libexec/helper-scripts/get_colors.sh
   source "${HELPER_SCRIPTS_PATH:-}/usr/libexec/helper-scripts/get_colors.sh"
   get_colors
}

cli_links_to_footnotes() {
   ## Convert GUI hyperlinks in a CLI message to footnotes: each
   ## <a href="url">text</a> becomes "text[N]" inline, and the URLs are listed
   ## under a trailing "Links:" section. This keeps URLs out of the flow text
   ## while preserving them (strip-markup would otherwise drop the href).
   ## An anchor whose text is already the URL is the exception: it is emitted
   ## as the bare URL inline with no footnote, since a footnote would only
   ## repeat that same URL. Echoes the transformed message. Handles quoted or
   ## unquoted href.
   local rest="$1" footer="" n=0 whole url text before after
   local re='<a href="?([^">]*)"?>([^<]*)</a>'
   while [[ "${rest}" =~ ${re} ]]; do
      whole="${BASH_REMATCH[0]}"
      url="${BASH_REMATCH[1]}"
      text="${BASH_REMATCH[2]}"
      before="${rest%%"${whole}"*}"   # text before the first occurrence
      after="${rest#*"${whole}"}"     # text after it
      if [ "${text}" = "${url}" ]; then
         ## Link text is the URL itself, so just include verbatim
         rest="${before}${url}${after}"
         continue
      fi
      n=$(( n + 1 ))
      rest="${before}${text}[${n}]${after}"
      footer="${footer}[${n}] ${url}"$'\n'
   done
   if [ "${n}" -gt 0 ]; then
      printf '%s\n' "${rest}"
      printf '%s\n' ''
      printf '%s\n' 'Links:'
      printf '%s' "${footer}"
   else
      ## No trailing newline needed, the --messagecli code adds one when
      ## printing to the terminal.
      printf '%s' "${rest}"
   fi
}

cli_translate_gui_markup() {
   ## Translate an HTML message into its CLI equivalent, in the order that must
   ## run before strip-markup.
   local text
   text="$1"

   ## Translate color tags (<font color="...">) to terminal ANSI colors.
   text="$(printf '%s' "${text}" | sed \
      -e "s/<font color=\"green\">/${green:-}/g" \
      -e "s/<font color=\"orange\">/${yellow:-}/g" \
      -e "s/<font color=\"yellow\">/${yellow:-}/g" \
      -e "s/<font color=\"red\">/${red:-}/g" \
      -e "s#</font>#${reset:-}#g")"

   ## Translate hyperlinks to footnote form.
   text="$(cli_links_to_footnotes "${text}")"

   ## Translate line break taks to real newlines, but beware that these tags
   ## are often accompanied by real newlines already.
   text="$(printf '%s' "${text}" | sed -zE -e 's#[[:space:]]*<br ?/?>[[:space:]]*#\n#g')"

   printf '%s' "${text}"
}

pretty_type_cli() {
   trap "error_handler" ERR

   if [ "$1" = "info" ]; then
      p_type="${green}INFO${reset:-}"
   elif [ "$1" = "warning" ]; then
      p_type="${red}WARNING${reset:-}"
   elif [ "$1" = "error" ]; then
      p_type="${red}${bold}ERROR${reset:-}"
   else
      p_type="${red}???${reset:-}"
   fi
}

pretty_type_x() {
   trap "error_handler" ERR

   local msgINFO msgWARNING msgERROR
   msgINFO="<p><span style=color:#008000>INFO</span>:"
   msgWARNING="<p><span style=color:#c00000>WARNING</span>:"
   msgERROR="<p><span style=font-weight:600;color:#ff0000>ERROR</span>:"

   local first_three
   first_three="${message:0:3}"
   if [ "${first_three}" = "<p>" ]; then
      local message_length remaining_chars message_without_p
      message_length="${#message}"
      remaining_chars="$(( message_length - 3 ))"
      message_without_p="${message:3:${remaining_chars}}"
      if [ "$1" = "info" ]; then
         message="${msgINFO} ${message_without_p}"
      elif [ "$1" = "warning" ]; then
         message="${msgWARNING} ${message_without_p}"
      elif [ "$1" = "error" ]; then
         message="${msgERROR} ${message_without_p}"
      fi
   fi
}

escalate_type_file() {
   ## Write $2 (info|warning|error) to the type file $1 without downgrading an
   ## existing higher severity
   local type_file new lock_fd
   type_file="$1"
   new="$2"

   (
      exec {lock_fd}>>"${type_file}.lock"
      flock --exclusive -- "${lock_fd}"

      old=""
      if [ -f "${type_file}" ]; then
         old="$(cat -- "${type_file}" 2>/dev/null)" || true
      fi

      case "${old}" in
         error)
            exit 0
            ;;
         warning)
           [ "${new}" = "error" ] || exit 0
            ;;
         info)
            [ "${new}" = "error" ] || [ "${new}" = "warning" ] || exit 0
            ;;
      esac

      if ! [ "${new}" = '' ]; then
         printf '%s\n' "${new}" > "${type_file}"
      fi
   )
}

stcat_message() {
   ## Sanitize a CLI message from stdin with stcat.
   if [ -n "${green:-}" ]; then
      env --unset=NO_COLOR COLORTERM=truecolor stcat
   else
      stcat
   fi
}

collector() {
   trap "error_handler" ERR

   ## Input sanitization summary:
   ## - identifier, progressbaridx: validated alphanumeric via check() (path traversal prevention)
   ## - icon, titlex, passivepopupqueuextitle, progressbartitlex: sanitize-string (plain text fields)
   ## - messagecli: strip-markup only (preserves terminal colors, strips HTML)
   ## - waitmessagecli, passivepopupqueuex, progressbarx message: sanitize-string
   ## - messagex: NOT sanitized (intentional - contains caller-constructed HTML for setHtml())

   ## {{ --identifier
   if ! msgcollector_check "${identifier}"; then
      printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable 'identifier' is empty or invalid."
      exit 1
   fi
   ## }}

   ## {{ --icon
   if [ ! "${icon}" = "" ]; then
      ## Debugging.
      #ls -la "${msgcollector_run_dir}"
      #test -r "${msgcollector_run_dir}"
      #test -w "${msgcollector_run_dir}"
      #test -r "${msgcollector_run_dir}/${identifier}_icon" || true "not readable"
      #test -w "${msgcollector_run_dir}/${identifier}_icon" || true "not writeable"
      #whoami
      ## NOTE: If not writeable, AppArmor issues?
      icon="$(sanitize-string -- nolimit "${icon}")"
      printf '%s\n' "${icon}" > "${msgcollector_run_dir}/${identifier}_icon"
   fi
   ## }}

   ## {{ --parentpid
   if [ "${parentpid}" = "" ]; then
      #printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable parentpid is empty."
      #exit 1
      ## Small hack, so not all applications get killed when pressing the cancel button
      ## such as timesync.
      parentpid="0000000000"
   fi
   ## Not at this point to avoid useless files.
   #printf '%s\n' "$parentpid" > "${msgcollector_run_dir}/${identifier}_${progressbaridx}_parentpid"
   ## }}

   ## {{ --titlecli
   if [ -n "${titlecli}" ]; then
      ## variable ${titlecli} exists
      ## TODO
      true "TODO: --titlecli not yet implemented in msgdispatcher"
      #printf '%s\n' "${titlecli}" > "${msgcollector_run_dir}/${identifier}_titlecli"
   fi
   ## }}

   ## {{ --titlex
   if [ -n "${titlex}" ]; then
      ## variable $titlex exists
      titlex="$(sanitize-string -- nolimit "${titlex}")"
      printf '%s\n' "${titlex}" > "${msgcollector_run_dir}/${identifier}_titlex"
   fi
   ## }}

   ## {{ --passivepopupqueuextitle
   if [ -n "${passivepopupqueuextitle}" ]; then
      ## variable $passivepopupqueuextitle exists
      passivepopupqueuextitle="$(sanitize-string -- nolimit "${passivepopupqueuextitle}")"
      printf '%s\n' "${passivepopupqueuextitle}" > "${msgcollector_run_dir}/${identifier}_passivepopupqueuextitle"
   fi
   ## }}

   ## {{ --progressbartitlex
   if [ -n "${progressbartitlex}" ]; then
      ## variable $progressbartitlex exists
      progressbartitlex="$(sanitize-string -- nolimit "${progressbartitlex}")"
      printf '%s\n' "${progressbartitlex}" > "${msgcollector_run_dir}/${identifier}_${progressbaridx}_progressbartitlex"
   fi
   ## }}

   ## {{ --typecli
   if [ "${messagecli}" = "1" ] && [ "${onlyecho}" != "1" ]; then
      escalate_type_file "${msgcollector_run_dir}/${identifier}_typecli" "${typecli}"
   fi
   ## }}

   ## {{ --typex
   if [ "${messagex}" = "1" ]; then
      escalate_type_file "${msgcollector_run_dir}/${identifier}_typex" "${typex}"
   fi
   ## }}

   ## {{ --messagex
   ## NOTE: $message is intentionally NOT sanitized here.
   ## messagex messages contain caller-constructed HTML (<p>, <span>, <br>, etc.)
   ## that is rendered via setHtml() in msgdispatcher_dispatch_x.
   ## sanitize-string would strip this HTML and break the GUI output.
   ## Callers are responsible for providing safe HTML content.
   if [ "${messagex}" = "1" ]; then
      pretty_type_x "${typex}"
      write_to_file="${msgcollector_run_dir}/${identifier}_messagex"
      append_to_file
   fi
   ## }}

   ## {{ --messagecli
   if [ "${messagecli}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_messagecli"
      if [ -n "${message}" ]; then
         ## variable $message exists
         ## Sanitize non-ASCII first.
         message="$(printf '%s' "${message}" | stcat_message)"
         ## Translate GUI markup to CLI.
         message="$(cli_translate_gui_markup "${message}")"
         ## Strip any remaining HTML tags.
         message="$(strip-markup "${message}")"
         ## Sanitize non-ASCII once more; strip-markup expands HTML entities,
         ## which can reintroduce escape sequences.
         message="$(printf '%s' "${message}" | stcat_message)"
         pretty_type_cli "${typecli}"
         message="[${p_type}] [${identifier}] ${message}"
         printf '%s\n' "${message}"
      fi
      append_to_file
   fi
   ## }}

   ## {{ --waitmessagecli
   if [ "${waitmessagecli}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_waitmessagecli"
      if [ -n "${message}" ]; then
         ## variable $message exists
         message="$(sanitize-string -- nolimit "${message}")"
         pretty_type_cli "${typecli}"
         message="[${p_type}] [${identifier}] ${message}"
         printf '%s\n' "${message}"
      fi
      append_to_file
   fi
   ## }}

   ## {{ --passivepopupqueuex
   if [ "${passivepopupqueuex}" = "1" ]; then
      if [ "${message}" = "" ]; then
         message="message was empty."
      fi
      message="$(sanitize-string -- nolimit "${message}")"
      if [ "${message}" = "" ]; then
         message="message was empty after sanitize-string."
      fi
      if [ -n "${typex}" ]; then
         escalate_type_file "${msgcollector_run_dir}/${identifier}_passivepopupqueuextype" "${typex}"
      fi
      write_to_file="${msgcollector_run_dir}/${identifier}_passivepopupqueuex"
      append_to_file
   fi
   ## }}

   ## {{ --progressx
   if [ ! "${progressx}" = "" ]; then
      if ! msgcollector_check "${progressbaridx}"; then
         error "Variable 'progressbaridx' is empty or invalid."
         exit 4
      fi

      ## Debugging.
      #caller="$(ps -p $PPID)" || true
      #printf '%s\n' "msgcollector: progressx: $progressx | caller: $PPID | $caller" >> /home/user/progresslog

      if [ "${verbose}" = "1" ]; then
         bash -x "${MSGCOLLECTOR_PATH:-}/usr/libexec/msgcollector/msgprogress" --identifier "${identifier}" --progressbaridx "${progressbaridx}" --progress "${progressx}"
      else
         ## & disown to prevent long waiting.
         "${MSGCOLLECTOR_PATH:-}/usr/libexec/msgcollector/msgprogress" --identifier "${identifier}" --progressbaridx "${progressbaridx}" --progress "${progressx}" & disown
      fi
   fi
   ## }}

   ## {{ --forceactive
   if [ "${forceactive}" = "1" ]; then
      touch -- "${msgcollector_run_dir}/${identifier}_forceactive"
   fi
   ## }}

   ## {{ --parenttty
   if [ -n "${parenttty}" ]; then
      printf '%s\n' "${parenttty}" > "${msgcollector_run_dir}/${identifier}_parenttty"
   fi
   ## }}

   ## {{ --progressbarx
   if [ "${progressbarx}" = "1" ]; then
      if ! msgcollector_check "${progressbaridx}"; then
         error "Variable 'progressbaridx' is empty or invalid."
         exit 4
      fi
      printf '%s\n' "${parentpid}" > "${msgcollector_run_dir}/${identifier}_${progressbaridx}_parentpid"
      message="$(sanitize-string -- nolimit "${message}")"
      write_to_file="${msgcollector_run_dir}/${identifier}_${progressbaridx}_progressbarx"
      append_to_file

      ## Cannot run from msgdispatcher due to user mismatch permission issues.
      "${MSGCOLLECTOR_PATH:-}/usr/libexec/msgcollector/msgprogressbar" --identifier "${identifier}" --progressbaridx "${progressbaridx}" --progressbartitlex "${progressbartitlex}" --message "${message}" & disown
   fi
   ## }}
}

append_to_file() {
   trap "error_handler" ERR

   if [ "${onlyecho}" = "1" ]; then
      true "onlyecho"
      return 0
   fi

   if [ -n "${message}" ]; then
      ## variable $message exists
      if [ "${newlinecli}" = "1" ]; then
         printf '%s\n' "
${message}" >> "${write_to_file}"
      elif [ "${messagex}" = 1 ]; then
         if [ "${nonewlinex}" = "1" ]; then
            printf '%s\n' "${message}" >> "${write_to_file}"
         else
            printf '%s\n' "${message}
" >> "${write_to_file}"
         fi
      else
         printf '%s\n' "${message}" >> "${write_to_file}"
      fi
   fi

   ## {{ --done
   if [ "${done}" = "1" ]; then
      touch -- "${write_to_file}_done"
   fi
   ## }}
}

return_status() {
   trap "error_handler" ERR

   ## {{ --identifier
   if ! msgcollector_check "${identifier}"; then
      printf '%s\n' "${BASH_SOURCE[0]} ERROR: variable identifier is empty or invalid."
      exit 1
   fi
   ## }}

   ## {{ --messagex
   if [ "${messagex}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_messagex"
      return_status_file
   fi
   ## }}

   ## {{ --messagecli
   if [ "${messagecli}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_messagecli"
      return_status_file
   fi
   ## }}

   ## {{ --waitmessagecli
   if [ "${waitmessagecli}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_waitmessagecli"
      return_status_file
   fi
   ## }}

   ## {{ --passivepopupqueuex
   if [ "${passivepopupqueuex}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_passivepopupqueuex"
      return_status_file
   fi
   ## }}

   ## {{ --progressbarx
   if [ "${progressbarx}" = "1" ]; then
      if ! msgcollector_check "${progressbaridx}"; then
         error "Variable progressbaridx is empty or invalid."
         exit 4
      fi
      write_to_file="${msgcollector_run_dir}/${identifier}_${progressbaridx}_progressbarx"
      return_status_file
   fi
   ## }}

   ## {{ --progressbarxrunning
   if [ "${progressbarxrunning}" = "1" ]; then
      if [ -f "${msgcollector_run_dir}/${identifier}_${progressbaridx}_yadprogresspid" ]; then
         ## || true to prevent race condition
         yad_progress_pid="$(cat -- "${msgcollector_run_dir}/${identifier}_${progressbaridx}_yadprogresspid")" || true
         if ! is_whole_number "${yad_progress_pid}"; then
            exit 1
         fi
         ## Check if still running.
         local ps__p_exit_code
         ps__p_exit_code="0"
         ps -p "${yad_progress_pid}" >/dev/null 2>/dev/null || { ps__p_exit_code="$?"; true; };
         if [ "${ps__p_exit_code}" = "0" ]; then
            exit 0
         else
            exit 1
         fi
      else
         exit 1
      fi
   fi
   ## }}

   ## {{ --progressbarxprogresstxtexisting
   if [ "${progressbarxprogresstxtexisting}" = "1" ]; then
      if [ -f "${msgcollector_run_dir}/${identifier}_${progressbaridx}_progresstxt" ]; then
         exit 0
      else
         exit 1
      fi
   fi
   ## }}

   ## {{ --typexstatus
   if [ "${typex_chosen}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_typex"
      if [ -f "${write_to_file}" ]; then
         local TYPE
         TYPE="$(cat -- "${write_to_file}")"
         printf '%s\n' "${TYPE}"
      fi
      exit 0
   fi
   ## }}

   ## {{ --typeclistatus
   if [ "${typecli_chosen}" = "1" ]; then
      write_to_file="${msgcollector_run_dir}/${identifier}_typecli"
      if [ -f "${write_to_file}" ]; then
         local TYPE
         TYPE="$(cat -- "${write_to_file}")"
         printf '%s\n' "${TYPE}"
      fi
      exit 0
   fi
   ## }}

   true "Fallback exit code, none of the above matched."
   exit 3
}

return_status_file() {
   trap "error_handler" ERR

   if [ -f "${write_to_file}" ]; then
      exit 0
   else
      exit 1
   fi
}

forget() {
   trap "error_handler" ERR

   ## & disown to prevent long waiting.
   ## Setting progress to 100, so any eventually still open progress bars get closed.
   ## Deactivated.
   ## Problematic if a new progress bar will be started quickly afterward.
   #/usr/libexec/msgcollector/msgprogress --identifier "$identifier" --progress "100" & disown

   local file_list=(
      "${msgcollector_run_dir}/${identifier}_icon"
      "${msgcollector_run_dir}/${identifier}_titlecli"
      "${msgcollector_run_dir}/${identifier}_titlex"
      "${msgcollector_run_dir}/${identifier}_passivepopupqueuextitle"
      "${msgcollector_run_dir}/${identifier}_passivepopupqueuextype"
      "${msgcollector_run_dir}/${identifier}_${progressbaridx}_progressbartitlex"
      "${msgcollector_run_dir}/${identifier}_typecli"
      "${msgcollector_run_dir}/${identifier}_typex"
      "${msgcollector_run_dir}/${identifier}_${progressbaridx}_progressbarx_done"
      "${msgcollector_run_dir}/${identifier}_${progressbaridx}_progressbarx"
      "${msgcollector_run_dir}/${identifier}_passivepopupqueuex_done"
      "${msgcollector_run_dir}/${identifier}_passivepopupqueuex"
      "${msgcollector_run_dir}/${identifier}_messagex_done"
      "${msgcollector_run_dir}/${identifier}_messagex"
      "${msgcollector_run_dir}/${identifier}_waitmessagecli_done"
      "${msgcollector_run_dir}/${identifier}_waitmessagecli"
      "${msgcollector_run_dir}/${identifier}_messagecli_done"
      "${msgcollector_run_dir}/${identifier}_messagecli"
      "${msgcollector_run_dir}/${identifier}_forceactive"
      "${msgcollector_run_dir}/${identifier}_parenttty"
   )

   ## Better not deleting
   ## "${msgcollector_run_dir}/${identifier}_${progressbaridx}_parentpid"
   ## "${msgcollector_run_dir}/${identifier}_${progressbaridx}_fifo"
   ## "${msgcollector_run_dir}/${identifier}_${progressbaridx}_progresstxt"
   ## "${msgcollector_run_dir}/${identifier}_${progressbaridx}_yadprogresspid"
   ## because that could confuse /usr/libexec/msgcollector/msgprogress.

   local file_name
   for file_name in "${file_list[@]}"; do
      if [ -e "${file_name}" ]; then
         safe-rm --force -- "${file_name}"
      fi
   done
   unset file_name
}

forgetwaitcli() {
   trap "error_handler" ERR

   local file_list=(
      "${msgcollector_run_dir}/${identifier}_waitmessagecli_done"
      "${msgcollector_run_dir}/${identifier}_waitmessagecli"
   )

   local file_name
   for file_name in "${file_list[@]}"; do
      if [ -e "${file_name}" ]; then
         safe-rm --force -- "${file_name}"
      fi
   done
   unset file_name
}

## Debugging.
#printf '%s\n' "${BASH_SOURCE[0]} $@
#" >> "/home/user/msgcollector"

main() {
   true "$0: START"
   trap "error_handler" ERR
   my_command_line_parameters="$*"

   colors

   parse_cmd_options "$@"

   # shellcheck source=./msgcollector_shared
   source "${MSGCOLLECTOR_PATH:-}/usr/libexec/msgcollector/msgcollector_shared"
   ## sets: ${msgcollector_run_dir}
   folder_init

   ## provides: msgcollector_check, is_whole_number
   # shellcheck source=./check
   source "${MSGCOLLECTOR_PATH:-}/usr/libexec/msgcollector/check"

   if [ "${forget}" = "1" ]; then
      forget
      exit 0
   fi

   if [ "${forgetwaitcli}" = "1" ]; then
      forgetwaitcli
      exit 0
   fi

   preparation

   if [ "${status}" = "1" ]; then
      return_status
   else
      collector
   fi

   true "$0: END"
}

if was_executed "${BASH_SOURCE[0]}"; then
   main "$@"
fi
