#!/bin/bash

#
# Name:         functions4.sh
# Version:      1.151
#
# (c) 2016-2024, Rapidseedbox, alexey@rapidseedbox.com
#

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

export SUPPORT

__VER="$( grep -m 1 -E '^#\s*Version:\s+' "./${LIBRARY}" | awk '{print $NF}' )"

LIB_VER="${__VER}"
APP_DB_VER="${__VER}"

export LANG="en_US"

if [ "${DEBUG}" = "yes" ] ; then
   set -x
else
   DEBUG="no"
fi
[ "${QUIET}" = "yes" ] || QUIET="no"
[ -z "${DIST_UPGRADE_ENABLED}" ] && DIST_UPGRADE_ENABLED="no"


# --------------------------------------------------------------------------
#
# Logging functions idea from
# http://www.cubicrace.com/2016/03/efficient-logging-mechnism-in-shell.html
#
# --------------------------------------------------------------------------


#LOGFILE=SystemOut.log
RSB_URL="$( sed -e 's|^\(.*\)/support$|\1|' <<< "${SUPPORT}" )"
#TMP_DIR="/tmp/support.$$"
#touch "${LOGFILE}"
#tmp="${0}.tmp.$$"

# User variables:
USERNAME="${1}"
[ -z "${USERNAME}" ] && USERNAME="user"
USERPASSWORD="${2}"
[ -z "${USERPASSWORD}" ] && USERPASSWORD="ChangeMe"
USERHOME="/home/${USERNAME}"

PASSWORDS_STORE_DIR="/var/local/passwd"
PASSWORDS_STORE="${PASSWORDS_STORE_DIR}/${USERNAME}"

if [ -e "${PASSWORDS_STORE}" ] ; then
    _password="$( awk -F: '$1 == "user" {print $2}' "${PASSWORDS_STORE}" 2> /dev/null )"
    [ -n "${_password}" ] && USERPASSWORD="${_password}"
fi

# Database information
APP_DB_UPDATE="yes"
APP_DB_DIR="/var/db/whmcs_client_installers"
[ ! -d "${APP_DB_DIR}" ] && mkdir -p "${APP_DB_DIR}" > /dev/null 2>&1
chmod 0700 "${APP_DB_DIR}" > /dev/null 2>&1
APP_DB="${APP_DB_DIR}/app.db"
APP_DB_WHMCS="${APP_DB_DIR}/app-whmcs.db"
APP_DB_WHMCS_JSON="${APP_DB_DIR}/app-whmcs.json"
if [ "${USERNAME}" != "user" ] ; then
    APP_DB="${APP_DB_DIR}/app-${USERNAME}.db"
    APP_DB_WHMCS="${APP_DB_DIR}/app-whmcs-${USERNAME}.db"
    APP_DB_WHMCS_JSON="${APP_DB_DIR}/app-whmcs-${USERNAME}.json"
fi
JSON_MSG=""

[ -e "${APP_DB_DIR}/.db.source" ] && rm -f "${APP_DB_DIR}/.db.source" > /dev/null 2>&1
_d="/var/local/whmcs_client_installers"
[ ! -d "${_d}" ] && mkdir -p "${_d}" > /dev/null 2>&1
sed -re 's#/support\s*$##' <<< "${SUPPORT}" 2> /dev/null > "${_d}/.db.source"


script_name="$( basename "${0}" )"
script_pkgname="$( sed -e 's|^[^_]*_\(.*\)\.sh$|\1|' <<< "${script_name}" )"

TMP_DIR="/var/tmp/${script_name}.installers.$$"
touch "${LOGFILE}"
tmp="${0}.tmp.$$"

case "${script_name}" in
  install_*)
      operation1="installing"
      operation2="Installation"
      ;;
  uninstall_*)
      operation1="uninstalling"
      operation2="Uninstallation"
      ;;
  upgrade_*)
      operation1="upgrading"
      operation2="Upgrade"
      ;;
  __*)
      operation1="maintenance"
      operation2="Maintenance"
      ;;
esac

MIN_HDD_SPACE_PCT="1"

APP_VENV="${APP_VENV:-${SYS_PKG_NAME:-$script_pkgname}}"

RSB_VENV="/opt/rsb-venvs"

GITHUB_RELEASE_TAG=""
GITHUB_RELEASE_VERSION=""
GITHUB_RELEASE_NAME=""
GITHUB_RELEASE_URL=""
GITHUB_RELEASE_ASSET_NAME=""
GITHUB_RELEASE_ASSET_URL=""
GITHUB_RELEASE_ASSET_API_URL=""
GITHUB_RELEASE_ASSET_ID=""

function L_LOG () {
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    echo "-----------------------------------------------------------------------------------------" \
        >> "${LOGFILE}"
}

function LL_LOG () {
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    echo "=========================================================================================" \
        >> "${LOGFILE}"
}

function L_LOG_START () {
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    echo -e "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" \
        >> "${LOGFILE}"
}

function L_LOG_FINISH () {
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    echo -e "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" \
        >> "${LOGFILE}"
}

function ENTRY(){
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    local cfn="${FUNCNAME[1]}"
    local tstamp="$( date -u +"%d-%m-%Y %T %Z" )"
    local msg="> ${cfn} ${FUNCNAME}"
    LL_LOG
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
}

function RETURN(){
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    local cfn="${FUNCNAME[1]}"
    local tstamp="$( date -u +"%d-%m-%Y %T %Z" )"
    local msg="< ${cfn} ${FUNCNAME}"
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
    LL_LOG
}

function INFO()
{
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    local msg="${1}"
    local tstamp="$( date -u +"%d-%m-%Y %T %Z" )"
    echo -e "[${tstamp}] [INFO]\t${msg}" >> "${LOGFILE}"
}

function WARN()
{
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    local msg="${1}"
    local tstamp="$( date -u +"%d-%m-%Y %T %Z" )"
    echo -e "[${tstamp}] [WARN]\t${msg}" >> "${LOGFILE}"
}

function ERROR()
{
    [ "${QUIET}" = "yes" -a "${DEBUG}" != "yes" ] && return
    local msg="${1}"
    local tstamp="$( date -u +"%d-%m-%Y %T %Z" )"
    echo -e "[${tstamp}] [ERROR]\t${msg}" >> "${LOGFILE}"
}

function DEBUG()
{
    [ "${DEBUG}" = "yes" ] || return
    local msg="${1}"
    local tstamp="$( date -u +"%d-%m-%Y %T %Z" )"
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
}

function INSTALLAPP_MSG()
{
    [ -n "${JSON_MSG}" ] && JSON_MSG="${JSON_MSG}; "
    JSON_MSG="${JSON_MSG}${1}"
    [ -z "${INSTALLAPP_TMP_DIR}" ] && return
    echo "${1}" >> "${INSTALLAPP_TMP_DIR}/install-app.msg"
}

# --------------------------------------------------------------
# RSB custom code
# --------------------------------------------------------------
#export IP="$( ip route get 8.8.8.8 | awk '/8.8.8.8/ {print $NF}' )"
#export IP="$( ip route get 8.8.8.8 | head -1 | sed -e 's/.*\s*src\s*\([0-9.]*\).*$/\1/' )"
export IP="$( ip -o route get to 8.8.8.8 | sed -nre 's/.*\s+src\s+([0-9.]+).*/\1/p' )"
export DEV="$( ip route get 8.8.8.8 | grep '8.8.8.8' | sed -e 's/.*dev//' | awk '{print $1}' )"
HOSTNAME="$( hostname )"
export DATE="$( date +%Y-%m-%d:%H:%M:%S )"
export DEBIAN_FRONTEND="noninteractive"


function LOG_INFO()
{
    local title
    local result=""
    [ -n "${1}" ] && title="${1}" || title="${JOB_NAME}"
    [ -n "${2}" ] && result="${2}"
    INFO "${title}"
    [ ! -z "${LOG}" ] && INFO "${LOG}"
    [ ! -z "${result}" ] && INFO "${result}"
    L_LOG
    LOG=""
}

function LOG_ERROR()
{
    local title
    local result=""
    [ -n "${1}" ] && title="${1}" || title="${JOB_NAME}"
    [ -n "${2}" ] && result="${2}"
    ERROR "${title}"
    [ ! -z "${LOG}" ] && ERROR "${LOG}"
    [ ! -z "${result}" ] && ERROR "${result}"
    L_LOG
    LOG=""
}

function LOG_DEBUG()
{
    local title
    local result=""
    [ -n "${1}" ] && title="${1}" || title="${JOB_NAME}"
    [ -n "${2}" ] && result="${2}"
    DEBUG "${title}"
    [ ! -z "${LOG}" ] && DEBUG "${LOG}"
    [ ! -z "${result}" ] && DEBUG "${result}"
    L_LOG
    LOG=""
}


function LOG_RC () {
    local _rc="${1}"
    if [ "${_rc}" != "0" ] ; then
        LOG_ERROR "${JOB_NAME}" "Failed! Rc: [${_rc}]"
    else
        LOG_INFO  "${JOB_NAME}" "Success. Rc: [${_rc}]"
    fi
}


function JOB_START()
{
    local title
    [ -n "${1}" ] && title="${1}" || title="${JOB_NAME}"
    L_LOG_START
    INFO "${title}: Started"
    L_LOG
}


function JOB_END()
{
    local title
    [ -n "${1}" ] && title="${1}" || title="${JOB_NAME}"
    L_LOG
    INFO "${title}: Finished"
    L_LOG_FINISH
}


function __run_log () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _cmd
    if [ -z "${1}" ] ; then
        ERROR "__run_log(): Argument missing!"
    else
        _cmd="${1}"
        INFO "Running command: [${_cmd}]"
        eval ${_cmd} >> "${LOGFILE}" 2>&1
        _rc=$?
        if [ ${_rc} -ne 0 ] ; then
            ERROR "Failed! Rc: [${_rc}]"
        else
            INFO "Success"
        fi
    fi
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function __run_nolog () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _cmd
    if [ -z "${1}" ] ; then
        ERROR "__run_nolog(): Argument missing!"
    else
        _cmd="${1}"
        eval ${_cmd} > /dev/null 2>&1
        _rc=$?
    fi
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function __update_json_status () {
    local _status="${1}"
    local _msg="$( sed -e 's/"/\\"/g' <<< "${2}" )"
    local _rc="${3}"
    [ -e "${APP_DB_WHMCS_JSON}" ] || return
    jq -c --arg s "${_status}" '.status = $s' "${APP_DB_WHMCS_JSON}" \
       | jq -c --arg m "${_msg}" '.message = $m' \
       | jq -c --arg r "${_rc}" '.rc = $r' \
       > "${APP_DB_WHMCS_JSON}.tmp"
    mv -f "${APP_DB_WHMCS_JSON}.tmp" "${APP_DB_WHMCS_JSON}"
}



function RM_FILES () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _main_rc=1
    local _rc=1
    local f
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Removing file(s)"
    if [ -z "${*}" ] ; then
        ERROR "RM_FILES(): Argument missing!"
    else
        _main_rc=0
        for f in "${@}" ; do
            INFO "Removing file: '${f}'"
            _rc=1
            if [ -e "${f}" -o -L "${f}" ] ; then
                __run_log "rm -f ${opts} \"${f}\""
                _rc=$?
                [ ${_rc} -ne 0 ] && _main_rc="${_rc}"
            else
                ERROR "'${f}': file not found!"
            fi
        done
    fi
    JOB_END "Removing file(s)"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_main_rc}
}


function RM_DIRS () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _main_rc=1
    local _rc=1
    local d
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Removing directory"
    if [ -z "${*}" ] ; then
        ERROR "RM_DIRS(): Argument missing!"
    else
        _main_rc=0
        for d in "${@}" ; do
            INFO "Removing directory: '${d}'"
            if [ -d "${d}" ] ; then
                if [ "${d}" = "/" ] ; then
                    ERROR "Can not remove this directory: '${d}'"
                    _main_rc=5
                else
                    __run_log "rm -rf ${opts} \"${d}\""
                    _rc=$?
                    [ ${_rc} -ne 0 ] && _main_rc=${_rc}
                fi
            else
                ERROR "'${d}': directory does not exists"
            fi
        done
    fi
    JOB_END "Removing directory"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_main_rc}
}


function APT_CLEAN () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local opts="-qq"
    [ ${DEBUG} = "yes" ] && opts=""
    JOB_START "Perform APT Clean"
    __run_log "apt-get ${opts} clean"
    _rc=$?
    JOB_END "Perform APT Clean"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function APT_UPDATE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local _main_rc=0
    local opts="-qq -o Dpkg::Options::='--force-confdef'"
    local _apt_get_extra_opts=""
    #
    [ ${DEBUG} = "yes" ] && opts=""
    JOB_START "Perform APT Update"
    __run_log "dpkg --configure -a --force-confdef"
    case "${LSB_VER}" in
      "Debian8"|"Debian9"|"Ubuntu16.04")
          ;;
      *)
          _apt_get_extra_opts="${_apt_get_extra_opts} --allow-releaseinfo-change"
          ;;
    esac
    __run_log "apt-get ${opts} ${_apt_get_extra_opts} update"
    _rc=$?
    if [ ${_rc} -ne 0 ] ; then
        INFO "Trying to fix GPG keys issues..."
        __run_log "apt-get ${_apt_get_extra_opts} update 2>&1 1> /dev/null \
            | sed -ne 's/.*NO_PUBKEY //p' \
            | while read key; do
                  if ! [[ \${keys[*]} =~ \"\${key}\" ]] ; then
                      apt-key adv --keyserver keyserver.ubuntu.com --recv-keys \${key} ;
                      keys+=(\${key}) ;
                  fi ;
              done"
        __run_log "apt-get ${opts} ${_apt_get_extra_opts} update"
        _rc=$?
        if [ ${_rc} -ne 0 ] ; then
            _main_rc=${_rc}
        fi
    fi
    #
    __run_log "apt-get ${opts} -y -f install"
    _rc=$?
    [ ${_rc} -ne 0 ] && _main_rc=${_rc}
    JOB_END "Perform APT Update"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_main_rc}
}


function APT_DIST_UPGRADE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local opts="-qq -o Dpkg::Options::='--force-confdef'"
    [ ${DEBUG} = "yes" ] && opts=""
    JOB_START "Perform APT Dist-Upgrade"
#    __run_log "yes N | apt-get ${opts} -y --allow-unauthenticated --allow-downgrades --allow-remove-essential --allow-change-held-packages dist-upgrade"
    __run_log "apt-get ${opts} -y --allow-unauthenticated --allow-downgrades --allow-remove-essential --allow-change-held-packages dist-upgrade < /dev/null 2>&1"
    _rc=$?
    JOB_END "Perform APT Dist-Upgrade"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function APT_INSTALL () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local opts="-o Dpkg::Options::='--force-confdef'"
    [ ${DEBUG} != "yes" ] && opts="${opts} -qq"
    [ "${ALLOW_UNAUTH_PKGS}" = "yes" ] && opts="${opts} --allow-unauthenticated"
    JOB_START "Perform APT Install"
    if [ -z "${1}" ] ; then
        ERROR "APT_INSTALL(): Argument(s) missing!"
    else
        __run_log "apt-get ${opts} -y install ${*}"
        _rc=$?
        # let's try to fix dependencies and re-try installation
        if [ ${_rc} -ne 0 ] ; then
            INFO "Previous installation attempt exited with RC:[${_rc}]"
            INFO "Trying to fix..."
            __run_log "apt-get ${opts} -y -f install"
            _rc=$?
            if [ ${_rc} -eq 0 ] ; then
                __run_log "apt-get ${opts} -y install ${*}"
                _rc=$?
            else
                INFO "Unable to fix :("
            fi
        fi
    fi
    JOB_END "Perform APT Install"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function APT_REMOVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local opts="-qq"
    [ ${DEBUG} = "yes" ] && opts=""
    JOB_START "Perform APT Remove"
    if [ -z "${1}" ] ; then
        ERROR "APT_REMOVE(): Argument(s) missing!"
    else
        __run_log "apt-get ${opts} -y purge ${*}"
        _rc=$?
    fi
    JOB_END "Perform APT Remove"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function APT_AUTOREMOVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local opts="-qq"
    [ ${DEBUG} = "yes" ] && opts=""
    JOB_START "Perform APT Auto-Remove"
    __run_log "apt-get ${opts} -y autoremove"
    _rc=$?
    JOB_END "Perform APT Auto-Remove"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function ADD_APT_PPA () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _repo
    _repo="${1}"
    JOB_START "Add APT Repository"
    if [ -z "${_repo}" ] ; then
        ERROR "ADD_APT_REPO(): Argument missing!"
    else
        __run_log "add-apt-repository -y \"${_repo}\""
        _rc=$?
    fi
    JOB_END "Add APT Repository"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function DEL_APT_REPO () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _repo
    _repo="${1}"
    JOB_START "Remove APT Repository"
    if [ -z "${_repo}" ] ; then
        ERROR "DEL_APT_REPO(): Argument missing!"
    else
        __run_log "add-apt-repository -y -r \"${_repo}\""
        _rc=$?
    fi
    JOB_END "Remove APT Repository"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function ADD_APT_REPO () {
    #    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1

    local _arg1 _arg2 _arg3 _arg4 _arg5 _arg6
    local _repo_line=""   # full "deb ..." line, if provided
    local _repo_url=""    # base URL, if building the line
    local _suite=""       # distro codename or suite, if building the line
    local _components=""  # components, if building the line
    local _keyref=""      # URL, local file path, "auto", or "none"
    local _name=""        # sources+keyring base name
    local _opts=""        # optional repo options inside [ ... ] (e.g. "arch=amd64")

    local _sources_dir="/etc/apt/sources.list.d"
    local _keyrings_dir="/etc/apt/keyrings"
    local _sources_file=""
    local _keyring_file=""

    _arg1="${1:-}"
    _arg2="${2:-}"
    _arg3="${3:-}"
    _arg4="${4:-}"
    _arg5="${5:-}"
    _arg6="${6:-}"

    JOB_START "Add APT Repo"

    # Usage:
    #   1) Full line form:
    #      ADD_APT_REPO "deb https://repo.example.com stable main" "<keyref|auto|none>" "[name]" "[opts]"
    #
    #   2) Builder form:
    #      ADD_APT_REPO "https://repo.example.com" "stable" "main" "<keyref|auto|none>" "[name]" "[opts]"
    #
    # keyref:
    #   - "https://..." : fetch key from URL (ASCII or binary)
    #   - "/path/file"  : read key from local file (ASCII or binary)
    #   - "auto"        : try to guess a key URL (best-effort)
    #   - "none"        : write repo without signed-by (NOT recommended; use only for trusted/internal cases)

    if [ -z "${_arg1}" ] ; then
        ERROR "ADD_APT_REPO(): Argument missing!"
        JOB_END "Add APT Repo"
        return 1
    fi

    # Detect whether arg1 is a full deb line or a repo URL
    case "${_arg1}" in
    deb\ *|deb-src\ *)
        _repo_line="${_arg1}"
        _keyref="${_arg2}"
        _name="${_arg3}"
        _opts="${_arg4}"
        ;;
    http://*|https://*)
        _repo_url="${_arg1}"
        _suite="${_arg2}"
        _components="${_arg3}"
        _keyref="${_arg4}"
        _name="${_arg5}"
        _opts="${_arg6}"
        ;;
    *)
        ERROR "ADD_APT_REPO(): First argument must be either a 'deb ...' line or an http(s) repo URL."
        JOB_END "Add APT Repo"
        return 1
        ;;
    esac

    # Validate builder-form required fields
    if [ -n "${_repo_url}" ] ; then
        if [ -z "${_suite}" ] || [ -z "${_components}" ] ; then
            ERROR "ADD_APT_REPO(): Missing suite/components for repo builder form."
            JOB_END "Add APT Repo"
            return 1
        fi
    fi

    # Key handling policy:
    # - Mandatory unless explicitly "none"
    # - If empty, fail (safer than silently trusting)
    if [ -z "${_keyref}" ] ; then
        ERROR "ADD_APT_REPO(): Key reference missing. Provide URL/path, or use 'auto', or 'none'."
        JOB_END "Add APT Repo"
        return 1
    fi

    # Derive name if not provided:
    # - If full line: try to derive from first URL-like token
    # - If builder: derive from repo host
    if [ -z "${_name}" ] ; then
        if [ -n "${_repo_url}" ] ; then
            _name="$( echo "${_repo_url}" | awk -F/ '{print $3}' | tr -cd 'A-Za-z0-9._-' )"
        else
            # Extract first token that looks like http(s)://... from the deb line
            _name="$( echo "${_repo_line}" | awk '{for(i=1;i<=NF;i++){if($i ~ /^https?:\/\//){gsub(/^https?:\/\//,"",$i); split($i,a,"/"); print a[1]; exit}}}' | tr -cd 'A-Za-z0-9._-' )"
        fi

        # Fallback if extraction failed
        [ -z "${_name}" ] && _name="custom-repo"
    fi

    _sources_file="${_sources_dir}/${_name}.list"
    _keyring_file="${_keyrings_dir}/${_name}.gpg"

    # Ensure dirs exist
    __run_log "install -d -m 0755 \"${_sources_dir}\""
    _rc=$?
    if [ "${_rc}" -ne 0 ] ; then
        JOB_END "Add APT Repo"
        return ${_rc}
    fi

    __run_log "install -d -m 0755 \"${_keyrings_dir}\""
    _rc=$?
    if [ "${_rc}" -ne 0 ] ; then
        JOB_END "Add APT Repo"
        return ${_rc}
    fi

    # If keyref is "auto", attempt to discover a key URL (best-effort)
    if [ "${_keyref}" = "auto" ] ; then
        if [ -z "${_repo_url}" ] ; then
            ERROR "ADD_APT_REPO(): keyref=auto requires builder form (repo URL provided), so I can try common key paths."
            JOB_END "Add APT Repo"
            return 1
        fi

        # Try common key locations (best-effort). First match wins.
        local _candidate=""
        local _found=""
        for _candidate in \
        "${_repo_url%/}/gpg.key" \
        "${_repo_url%/}/key.gpg" \
        "${_repo_url%/}/repo.gpg" \
        "${_repo_url%/}/archive.key" \
        "${_repo_url%/}/archive.gpg" \
        "${_repo_url%/}/KEY.gpg" \
        "${_repo_url%/}/KEY" \
        "${_repo_url%/}/public.key" \
        "${_repo_url%/}/public.gpg" \
        "${_repo_url%/}/Release.key" \
        "${_repo_url%/}/keys/repo.gpg" \
        "${_repo_url%/}/keys/repo.key" \
        "${_repo_url%/}/keys/public.gpg" \
        "${_repo_url%/}/keys/public.key" \
        ; do
            # Use spider to avoid downloading junk
            if wget -q --spider "${_candidate}" >/dev/null 2>&1 ; then
                _found="${_candidate}"
                break
            fi
        done

        if [ -z "${_found}" ] ; then
            ERROR "ADD_APT_REPO(): Could not auto-discover a key URL under ${_repo_url}. Provide the key URL/path explicitly."
            JOB_END "Add APT Repo"
            return 1
        fi

        _keyref="${_found}"
    fi

    # Install key unless explicitly "none"
    if [ "${_keyref}" != "none" ] ; then
        case "${_keyref}" in
        http://*|https://*)
        # Download -> dearmor -> keyring
            __run_log "wget -qO - \"${_keyref}\" | gpg --dearmor | tee \"${_keyring_file}\" >/dev/null"
            _rc=$?
            ;;
        /*)
        # Local file -> dearmor -> keyring
            __run_log "cat \"${_keyref}\" | gpg --dearmor | tee \"${_keyring_file}\" >/dev/null"
            _rc=$?
            ;;
        *)
            ERROR "ADD_APT_REPO(): keyref must be a URL, an absolute local path, 'auto', or 'none'."
            JOB_END "Add APT Repo"
            return 1
            ;;
        esac

        if [ "${_rc}" -ne 0 ] ; then
            JOB_END "Add APT Repo"
            return ${_rc}
        fi

        __run_log "chmod 0644 \"${_keyring_file}\""
        _rc=$?
        if [ "${_rc}" -ne 0 ] ; then
            JOB_END "Add APT Repo"
            return ${_rc}
        fi
    fi

    # Build repo line if needed, and inject signed-by unless keyref=none
    if [ -z "${_repo_line}" ] ; then
        if [ -n "${_opts}" ] ; then
            if [ "${_keyref}" != "none" ] ; then
                _repo_line="deb [${_opts} signed-by=${_keyring_file}] ${_repo_url} ${_suite} ${_components}"
            else
                _repo_line="deb [${_opts}] ${_repo_url} ${_suite} ${_components}"
            fi
        else
            if [ "${_keyref}" != "none" ] ; then
                _repo_line="deb [signed-by=${_keyring_file}] ${_repo_url} ${_suite} ${_components}"
            else
                _repo_line="deb ${_repo_url} ${_suite} ${_components}"
            fi
        fi
    else
        # Full line mode: if keyref != none, ensure it includes signed-by.
        # If it already has [ ... ], append signed-by if missing. Otherwise, inject a new [signed-by=...].
        if [ "${_keyref}" != "none" ] ; then
            if echo "${_repo_line}" | grep -q '\[.*signed-by=' ; then
                : # already present
            elif echo "${_repo_line}" | grep -q '^\s*deb\s\+\[.*\]\s' ; then
                _repo_line="$( echo "${_repo_line}" | sed "s/^\(\s*deb\s\+\[\)\(.*\)\(\]\s\)/\1\2 signed-by=${_keyring_file}\3/" )"
            else
                _repo_line="$( echo "${_repo_line}" | sed "s/^\(\s*deb\s\+\)/\1[signed-by=${_keyring_file}] /" )"
            fi
        fi

        # If opts were provided in full-line mode, merge them (optional behavior).
        # We only merge if the line has [] already or can be injected; and we don't override existing arch/signed-by.
        if [ -n "${_opts}" ] ; then
            if echo "${_repo_line}" | grep -q '^\s*deb\s\+\[.*\]\s' ; then
                # Append opts if not already present as a substring (best-effort, not a full parser)
                echo "${_repo_line}" | grep -qF "${_opts}" || _repo_line="$( echo "${_repo_line}" | sed "s/^\(\s*deb\s\+\[\)\(.*\)\(\]\s\)/\1\2 ${_opts}\3/" )"
            else
                _repo_line="$( echo "${_repo_line}" | sed "s/^\(\s*deb\s\+\)/\1[${_opts}] /" )"
            fi
        fi
    fi

    # Write sources file
    __run_log "printf '%s\n' \"${_repo_line}\" > \"${_sources_file}\""
    _rc=$?
    if [ "${_rc}" -ne 0 ] ; then
        JOB_END "Add APT Repo"
        return ${_rc}
    fi

    __run_log "chmod 0644 \"${_sources_file}\""
    _rc=$?

    JOB_END "Add APT Repo"
    #    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function YUM_DIST_UPGRADE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform YUM Dist-Upgrade"
    case "${LSB_VER}" in
      "CentOSStream8")
#          opts="${opts} --skip-broken --nobest"
          opts="${opts} --nobest"
    esac
    __run_log "yum ${opts} -y update"
    _rc=$?
    JOB_END "Perform YUM Dist-Upgrade"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function YUM_INSTALL () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform RPM (url) Install"
    if [ -z "${1}" ] ; then
        ERROR "YUM_INSTALL(): Argument(s) missing!"
    else
        if [ "${1}" = "--url" -o "${1}" = "-u" ] ; then
            if [ -z "${1}" ] ; then
                ERROR "YUM_INSTALL() URL: Argument missing!"
            else
                __run_log "rpm ${opts} -Uh \"${2}\""
                _rc=$?
            fi
        else
            __run_log "yum ${opts} -y install ${*}"
            _rc=$?
        fi
    fi
    JOB_END "Perform RPM (url) Install"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function YUM_GROUPINSTALL () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local g
    local _main_rc=1
    local _rc
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform YUM Group Install"
    if [ -z "${1}" ] ; then
        ERROR "YUM_GROUPINSTALL(): Argument(s) missing!"
    else
        _main_rc=0
        for g in "${@}" ; do
            __run_log "yum ${opts} -y groupinstall \"${g}\""
            _rc=$?
            [ ${_rc} -ne 0 ] && _main_rc=${_rc}
        done
    fi
    JOB_END "Perform YUM Group Install $* "
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_main_rc}
}


function YUM_REMOVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform YUM Remove"
    if [ -z "${1}" ] ; then
        ERROR "YUM_REMOVE(): Argument(s) missing!"
    else
        __run_log "yum ${opts} -y remove ${*}"
        _rc=$?
    fi
    JOB_END "Perform YUM Remove"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function YUM_GROUPREMOVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local g
    local _main_rc=1
    local _rc
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform YUM Group Remove"
    if [ -z "${1}" ] ; then
        ERROR "YUM_GROUPREMOVE(): Argument(s) missing!"
    else
        _main_rc=0
        for g in "${@}" ; do
            LOG=""
            __run_log "yum ${opts} -y groupremove \"${g}\""
            _rc=$?
            [ ${_rc} -ne 0 ] && _main_rc=${_rc}
        done
    fi
    JOB_END "Perform YUM Group Remove"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_main_rc}
}


function YUM_AUTOREMOVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local opts
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform YUM Auto-Remove"
    LOG=""
    __run_log "yum ${opts} -y autoremove"
    _rc=$?
    JOB_END "Perform YUM Auto-Remove"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function YUM_CLEAN () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Perform YUM Clean ALL"
    LOG=""
    __run_log "yum ${opts} -y clean all"
    _rc=$?
    JOB_END "Perform YUM Clean ALL"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function GIT_CLONE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _url
    local _dst
    local _msg
    local opts="-q"
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Cloning repository"
    if [ -z "${1}" ] ; then
        ERROR "GIT_CLONE(): Argument missing!"
    else
        _url="${1}"
        _dst="${2}"
        _msg="Cloning repository: '${_url}'"
        [ ! -z "${_dst}" ] && _msg="${_msg} -> '${_dst}'"
        INFO "${s}"
        __run_log "git clone ${opts} \"${_url}\" \"${_dst}\""
        _rc=$?
    fi
    JOB_END "Cloning repository"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}

function GITHUB_RELEASE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _repo
    local _ref="latest"
    local _arch=""
    local _type=""
    local _pattern=""
    local _api_url=""
    local _release_json=""
    local _arch_re=""
    local _type_re=""
    local _asset_json=""

    JOB_START "GitHub Release"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "GITHUB_RELEASE(): Argument(s) missing! Usage: GITHUB_RELEASE <owner/repo> [latest|tag] [arch] [type] [asset-name-regex]"
            break
        fi

        _repo="${1}"
        [ -n "${2}" ] && _ref="${2}"
        [ -n "${3}" ] && _arch="${3}"
        [ -n "${4}" ] && _type="${4}"
        [ -n "${5}" ] && _pattern="${5}"

        if ! command -v curl > /dev/null 2>&1 ; then
            ${PKG_INSTALL} "curl" || break
        fi
        if ! command -v jq > /dev/null 2>&1 ; then
            ${PKG_INSTALL} "jq" || break
        fi

        if [ "${_ref}" = "latest" ] ; then
            _api_url="https://api.github.com/repos/${_repo}/releases/latest"
        else
            _api_url="https://api.github.com/repos/${_repo}/releases/tags/${_ref}"
        fi

        INFO "Resolving GitHub release: '${_repo}', ref='${_ref}', arch='${_arch}', type='${_type}', pattern='${_pattern}'"
        if [ -n "${GITHUB_TOKEN}" ] ; then
            _release_json="$( curl -4 -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "${_api_url}" 2> /dev/null )"
        else
            _release_json="$( curl -4 -fsSL -H "Accept: application/vnd.github+json" "${_api_url}" 2> /dev/null )"
        fi
        [ -z "${_release_json}" ] && break

        GITHUB_RELEASE_TAG="$( jq -r '.tag_name // empty' <<< "${_release_json}" )"
        GITHUB_RELEASE_VERSION="${GITHUB_RELEASE_TAG}"
        GITHUB_RELEASE_NAME="$( jq -r '.name // empty' <<< "${_release_json}" )"
        GITHUB_RELEASE_URL="$( jq -r '.html_url // empty' <<< "${_release_json}" )"
        [ -z "${GITHUB_RELEASE_TAG}" ] && break

        case "$( tr '[:upper:]' '[:lower:]' <<< "${_arch}" )" in
          amd64|x86_64) _arch_re="(amd64|x86_64)" ;;
          arm64|aarch64) _arch_re="(arm64|aarch64)" ;;
          armv7|armhf) _arch_re="(armv7|armhf)" ;;
          armv6) _arch_re="(armv6)" ;;
          i386|386|x86) _arch_re="(i386|386|x86)" ;;
          "") _arch_re="" ;;
          *) _arch_re="${_arch}" ;;
        esac

        case "$( tr '[:upper:]' '[:lower:]' <<< "${_type}" )" in
          deb|debian) _type_re="\\.deb$" ;;
          apk|alpine) _type_re="\\.apk$" ;;
          rpm) _type_re="\\.rpm$" ;;
          tar|tgz|tar.gz) _type_re="\\.tar\\.gz$|\\.tgz$" ;;
          tar.xz|txz) _type_re="\\.tar\\.xz$|\\.txz$" ;;
          tar.bz2|tbz2) _type_re="\\.tar\\.bz2$|\\.tbz2$" ;;
          zip) _type_re="\\.zip$" ;;
          7z) _type_re="\\.7z$" ;;
          exe) _type_re="\\.exe$" ;;
          dmg) _type_re="\\.dmg$" ;;
          pkg) _type_re="\\.pkg$" ;;
          appimage) _type_re="\\.appimage$" ;;
          binary|bin) _type_re="(^[^.]+$|\\.bin$)" ;;
          "") _type_re="" ;;
          *) _type_re="${_type}" ;;
        esac

        _asset_json="$( jq -c \
            --arg arch_re "${_arch_re}" \
            --arg type_re "${_type_re}" \
            --arg pat_re "${_pattern}" \
            '[ .assets[]?
              | select(
                  (($arch_re == "") or (.name | test($arch_re; "i")))
                  and
                  (($type_re == "") or (.name | test($type_re; "i")))
                  and
                  (($pat_re == "") or (.name | test($pat_re; "i")))
                )
            ][0] // empty' <<< "${_release_json}" )"

        if [ -n "${_asset_json}" ] ; then
            GITHUB_RELEASE_ASSET_NAME="$( jq -r '.name // empty' <<< "${_asset_json}" )"
            GITHUB_RELEASE_ASSET_URL="$( jq -r '.browser_download_url // empty' <<< "${_asset_json}" )"
            GITHUB_RELEASE_ASSET_API_URL="$( jq -r '.url // empty' <<< "${_asset_json}" )"
            GITHUB_RELEASE_ASSET_ID="$( jq -r '.id // empty' <<< "${_asset_json}" )"
            echo "${GITHUB_RELEASE_ASSET_URL}"
        else
            GITHUB_RELEASE_ASSET_NAME=""
            GITHUB_RELEASE_ASSET_URL=""
            GITHUB_RELEASE_ASSET_API_URL=""
            GITHUB_RELEASE_ASSET_ID=""
            echo "${GITHUB_RELEASE_URL}"
        fi

        INFO "GitHub release resolved: tag='${GITHUB_RELEASE_TAG}', asset='${GITHUB_RELEASE_ASSET_NAME}'"
        _rc=0
        break
    done
    JOB_END "GitHub Release"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CHOWN_FILE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local f
    local _owner
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Changing owner"
    if [ -z "${2}" ] ; then
        ERROR "CHOWN_FILE(): Argument(s) missing!"
    else
        _owner="${1}"
        f="${2}"
        INFO "Changing owner of '${f}' file -> '${_owner}'"
        if [ -e "${f}" ] ; then
            __run_log "chown ${opts} ${_owner} \"${f}\""
            _rc=$?
        else
            ERROR "'${f}': file does not exists"
        fi
    fi
    JOB_END "Changing owner"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CHOWN_DIR () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local d
    local _owner
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Changing owner"
    if [ -z "${2}" ] ; then
        ERROR "CHOWN_DIR(): Argument(s) missing!"
    else
        _owner="${1}"
        d="${2}"
        INFO "Changing owner of '${d}' directory -> '${_owner}'"
        if [ -d "${d}" ] ; then
            __run_log "chown -R ${opts} ${_owner} \"${d}\""
            _rc=$?
        else
            ERROR "'${d}': Directory does not exists"
        fi
    fi
    JOB_END "Changing owner"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CHMOD_FILE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local f
    local _mode
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Changing mode"
    if [ -z "${2}" ] ; then
        ERROR "CHMOD_FILE(): Argument(s) missing!"
    else
        _mode="${1}"
        f="${2}"
        INFO "Changing mode of '${f}' -> '${_mode}'"
        if [ -e "${f}" ] ; then
            __run_log "chmod ${opts} ${_mode} \"${f}\""
            _rc=$?
        else
            ERROR "'${f}': file does not exists"
        fi
    fi
    JOB_END "Changing mode"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CHMOD_DIR () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local d
    local _mode
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Changing mode"
    if [ -z "${2}" ] ; then
        ERROR "CHMOD_DIR(): Argument(s) missing!"
    else
        _mode="${1}"
        d="${2}"
        INFO "Changing mode of '${d}' -> '${_mode}'"
        if [ -d "${d}" ] ; then
            __run_log "chmod -R ${opts} ${_mode} \"${d}\""
            _rc=$?
        else
            ERROR "'${d}': directory does not exists"
        fi
    fi
    JOB_END "Changing mode"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CP_FILE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _src
    local _dst
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Copy file"
    if [ -z "${2}" ] ; then
        ERROR "CP_FILE(): Argument(s) missing!"
    else
        _src="${1}"
        _dst="${2}"
        INFO "Copying '${_src}' -> '${_dst}'"
        if [ -e "${_src}" ] ; then
            __run_log "cp -pf ${opts} \"${_src}\" \"${_dst}\""
            _rc=$?
        else
            ERROR "'${_src}': file does not exists"
        fi
    fi
    JOB_END "Copy file"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CP_DIR () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _src
    local _dst
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Copy directory"
    if [ -z "${2}" ] ; then
        ERROR "CP_DIR(): Argument(s) missing!"
    else
        _src="${1}"
        _dst="${2}"
        INFO "Copying '${_src}' -> '${_dst}'"
        if [ -e "${_src}" ] ; then
            __run_log "cp -rpf ${opts} \"${_src}\" \"${_dst}\""
            _rc=$?
        else
            ERROR "'${_src}': file does not exists"
        fi
    fi
    JOB_END "Copy directory"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function MV () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _src
    local _dst
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Move"
    if [ -z "${2}" ] ; then
        ERROR "MV(): Argument(s) missing!"
    else
        _src="${1}"
        _dst="${2}"
        INFO "Moving '${_src}' -> '${_dst}'"
        if [ -e "${_src}" ] ; then
            __run_log "mv -f ${opts} \"${_src}\" \"${_dst}\""
            _rc=$?
        else
            ERROR "'${_src}': file does not exists"
        fi
    fi
    JOB_END "Move"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function LN () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _tgt
    local _lnk
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Create symbolic link"
    if [ -z "${2}" ] ; then
        ERROR "LN(): Argument(s) missing!"
    else
        _tgt="${1}"
        _lnk="${2}"
        INFO "Creating symbolic link: '${_lnk}' -> '${_tgt}'"
        __run_log "ln -fs ${opts} \"${_tgt}\" \"${_lnk}\""
        _rc=$?
    fi
    JOB_END "Create symbolic link"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function SVC_ENABLE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Enable service"
    if [ -z "${1}" ] ; then
        ERROR "SVC_ENABLE(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        INFO "Enabling service: '${_svc}'"
        if [ "${SVC_SYSTEM}" = "systemd" ] ; then
            __run_log "systemctl enable \"${_svc}\""
            _rc=$?
        elif [ "${SVC_SYSTEM}" = "init" ] ; then
            __run_log "chkconfig --list \"${_svc}\" > /dev/null 2>&1 \
                          || chkconfig --add \"${_svc}\" 2>&1 ; \
                             chkconfig \"${_svc}\" on 2>&1 ; \
                             chkconfig --list \"${_svc}\""
            _rc=$?
        fi
    fi
    JOB_END "Enable service"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function SVC_DISABLE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Disable service"
    if [ -z "${1}" ] ; then
        ERROR "SVC_DISABLE(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        INFO "Disabling service: '${_svc}'"
        if [ "${SVC_SYSTEM}" = "systemd" ] ; then
            __run_log "systemctl disable \"${_svc}\""
            _rc=$?
        elif [ "${SVC_SYSTEM}" = "init" ] ; then
            __run_log "chkconfig \"${_svc}\" off"
            _rc=$?
        fi
    fi
    JOB_END "Disable service"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function SVC_START () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Start service"
    if [ -z "${1}" ] ; then
        ERROR "SVC_START(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        INFO "Starting service: '${_svc}'"
        __run_log "/bin/bash -c \"/usr/sbin/service ${_svc} start\""
        _rc=$?
    fi
    JOB_END "Start service"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function SVC_STOP () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Stop service"
    if [ -z "${1}" ] ; then
        ERROR "SVC_STOP(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        INFO "Stopping service: '${_svc}'"
        __run_log "/bin/bash -c \"/usr/sbin/service ${_svc} stop\" 2>&1"
        _rc=$?
    fi
    JOB_END "Stop service"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function SVC_RESTART () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Restart service"
    if [ -z "${1}" ] ; then
        ERROR "SVC_RESTART(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        INFO "Restarting service: '${_svc}'"
        __run_log "/bin/bash -c \"/usr/sbin/service ${_svc} restart\""
        _rc=$?
    fi
    JOB_END "Restart service"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}

function SVC_RELOAD () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Reload service"
    if [ -z "${1}" ] ; then
        ERROR "SVC_RELOAD(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        INFO "Reload service: '${_svc}'"
        __run_log "/bin/bash -c \"/usr/sbin/service ${_svc} reload\""
        _rc=$?
    fi
    JOB_END "Reload service"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}

function SVC_STATUS () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _svc
    JOB_START "Service status"
    if [ -z "${1}" ] ; then
        ERROR "SVC_STATUS(): Argument missing!"
    else
        _svc="${1}"
        # Tweaks for functions.sh v1 library clients
        [ "${_svc}" = "apache2" ] && _svc="${APACHE_SVC}"
        #
        if [ "${DEBUG}" = "yes" ] ; then
            INFO "Getting service status: '${_svc}'"
            __run_log "/bin/bash -c \"/usr/sbin/service ${_svc} status\""
        else
            __run_nolog "/bin/bash -c \"/usr/sbin/service ${_svc} status\""
        fi
        _rc=$?
    fi
    JOB_END "Service status"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function SVC_ISACTIVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    JOB_START "Service ISACTIVE"
    if [ -z "${1}" ] ; then
        ERROR "SVC_ISACTIVE(): Argument missing!"
    else
        __run_nolog "systemctl is-active '${1}'"
        _rc=$?
    fi
    JOB_END "Service ISACTIVE"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function SVC_ISENABLED () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    JOB_START "Service ISENABLED"
    if [ -z "${1}" ] ; then
        ERROR "SVC_ISENABLED(): Argument missing!"
    else
        __run_nolog "systemctl is-enabled '${1}'"
        _rc=$?
    fi
    JOB_END "Service ISENABLED"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function SVC_MASK () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    JOB_START "Service MASK"
    if [ -z "${1}" ] ; then
        ERROR "SVC_MASK(): Argument missing!"
    else
        __run_nolog "systemctl mask '${1}'"
        _rc=$?
    fi
    JOB_END "Service MASK"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function SVC_UNMASK () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    JOB_START "Service UNMASK"
    if [ -z "${1}" ] ; then
        ERROR "SVC_UNMASK(): Argument missing!"
    else
        __run_nolog "systemctl unmask '${1}'"
        _rc=$?
    fi
    JOB_END "Service UNMASK"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}



function SYSTEMD_RELOAD () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    JOB_START "Reloading SystemD configuration"
    LOG=""
    __run_log "systemctl daemon-reload"
    _rc=$?
    JOB_END "Reloading SystemD configuration"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function MKDIR () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local d
    local opts=""
    if [ -d "${1}" ] ; then
        INFO "Directory already exist: '${1}'"
        return 0
    fi
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "Create directory"
    if [ -z "${*}" ] ; then
        ERROR "MKDIR(): Argument missing!"
    else
        LOG=""
        d="${1}"
        _rc=0
        INFO "Creating directory: '${d}'"
        if [ ! -d "${d}" ] ; then
            __run_log "mkdir -p ${opts} \"${d}\""
            _rc=$?
        else
            ERROR "'${d}': directory already exists"
        fi
    fi
    JOB_END "Create directory"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function MKDIR_TMP () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=0
    if [ ! -d "${TMP_DIR}" ] ; then
#        MKDIR "${TMP_DIR}"
       __run_nolog "mkdir -p ${TMP_DIR}"
        _rc=$?
    fi
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function WGET () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _url
    local opts="-q --no-check-certificate"
    local path="-P ${TMP_DIR}/"
    local _log="yes"
    [ "${DEBUG}" = "yes" ] && opts="-4 -t 5 -T 10 -v -S --no-check-certificate"
    if [ "${1}" = "--nolog" ] ; then
        _log="no"
        shift
    fi
    [ "${_log}" = "yes" ] && JOB_START "WGET"
    if [ -z "${1}" ] ; then
        ERROR "WGET(): Argument(s) missing!"
    else
        LOG=""
        if [ ! -z "${2}" ] ; then
            path="-O ${2}"
        fi
        _url="${1}"
        [ "${_log}" = "yes" ] && INFO "Fetching: '${_url}'"
        if [ "${_log}" = "yes" ] ; then
            __run_log "wget ${opts} ${path} ${_url} 2>&1"
            _rc=$?
        else
            __run_nolog "wget ${opts} ${path} ${_url} 2>&1"
            _rc=$?
        fi
    fi
    [ "${_log}" = "yes" ] && JOB_END "WGET"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}

# Kept for legacy purposes
function TAR_ZXF () {
    TAR_XF "$@"
}

function TAR_XF () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _tar
    local opts=""
    local path="."
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "TAR XF"
    if [ -z "${1}" ] ; then
        ERROR "TAR_XF(): Argument missing!"
    else
        if [ ! -z "${2}" ] ; then
            path="${2}"
        fi
        _tar="${1}"
        INFO "Extracting: '${_tar}' -> '${path}'"
        if [ ! -e "${_tar}" ] ; then
            ERROR "TAR_XF(): '${_tar}': file not found!"
        else
            __run_log "tar ${opts} -xf \"${_tar}\" -C \"${path}\""
            _rc=$?
        fi
    fi
    JOB_END "TAR XF"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function UNZIP () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _zip
    local path="."
    JOB_START "UNZIP"
    if [ -z "${1}" ] ; then
        ERROR "UNZIP(): Argument missing!"
    else
        if [ ! -z "${2}" ] ; then
            path="${2}"
        fi
        _zip="${1}"
        INFO "Unzipping: '${_zip}' -> '${path}'"
        if [ ! -e "${_zip}" ] ; then
            ERROR "UNZIP(): '${_zip}': file not found!"
        else
            __run_log "unzip -o -u -d \"${path}\" \"${_zip}\""
            _rc=$?
        fi
    fi
    JOB_END "UNZIP"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function DPKG_CHECK () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _pkg
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-D1"
    if [ -z "${1}" ] ; then
        ERROR "DPKG_CHECK(): Argument missing!"
    else
        _pkg="${1}"
        if [ ${DEBUG} = "yes" ] ; then
            ${__run_cmd} "dpkg ${opts} -s ${_pkg}"
        else
            ${__run_cmd} "dpkg ${opts} -s ${_pkg} > /dev/null 2>&1"
        fi
        _rc=$?
    fi
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function DPKG_VER () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local s
    if [ ${DEBUG} = "yes" ] ; then
        s="$( dpkg -s "${1}" | grep -m 1 -e '^Version:.*$' | awk '{print $NF}' )"
        _rc=$?
        DEBUG "DPKG_VER('${1}') -> '${s}'"
    else
        s="$( dpkg -s "${1}" 2> /dev/null | grep -m 1 -e '^Version:.*$' | awk '{print $NF}' )"
        _rc=$?
    fi
    echo "${s}"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function DPKG_INSTALL () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _pkg
    local a_opts="-o Dpkg::Options::='--force-confdef'"
    local d_opts="--force-confdef"
    if [ ${DEBUG} != "yes" ] ; then
        a_opts="${a_opts} -qq"
        d_opts="${d_opts} -D1"
    fi
    JOB_START "DPKG Install"
    if [ -z "${1}" ] ; then
        ERROR "DPKG_INSTALL(): Argument missing!"
    else
        _pkg="${1}"
        INFO "Installing: '${_pkg}'"
        if [ ! -e "${_pkg}" ] ; then
            ERROR "'${_pkg}': file not found!"
        else
#            __run_log "yes N | dpkg ${d_opts} -i \"${_pkg}\""
            __run_log "dpkg ${d_opts} -i \"${_pkg}\" < /dev/null 2>&1"
            _rc=$?
            # let's try to fix dependencies and re-try installation
            if [ ${_rc} -ne 0 ] ; then
                INFO "Previous installation attempt exited with RC:[${_rc}]"
                INFO "Trying to fix..."
#                __run_log "yes N | apt-get ${a_opts} -y -f install"
                __run_log "apt-get ${a_opts} -y -f install < /dev/null 2>&1"
                _rc=$?
                if [ ${_rc} -eq 0 ] ; then
                    __run_log "yes N | dpkg ${d_opts} -i \"${_pkg}\""
                    _rc=$?
                else
                    INFO "Unable to fix :("
                fi
            fi
        fi
    fi
    JOB_END "DPKG Install"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function DPKG_REMOVE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _pkg
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-D1"
    JOB_START "DPKG Remove"
    if [ -z "${1}" ] ; then
        ERROR "DPKG_REMOVE(): Argument missing!"
    else
        _pkg="${1}"
        INFO "Removing: '${_pkg}'"
        __run_log "dpkg ${opts} --purge \"${_pkg}\""
        _rc=$?
    fi
    JOB_END "DPKG Remove"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function RPM_CHECK () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _pkg
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    if [ -z "${1}" ] ; then
        ERROR "DPKG_CHECK(): Argument missing!"
    else
        _pkg="${1}"
        ${__run_cmd} "rpm ${opts} -q \"${_pkg}\""
        _rc=$?
    fi
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function RPM_VER () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc
    local s
    s="$( rpm -q --qf "%{PROVIDEVERSION}" "${1}" )"
    [ -z "${s}" ] \
        && s="$( rpm -q --qf "%{VERSION}" "${1}" )"
    _rc=$?
    [ "${DEBUG}" = "yes" ] && DEBUG "RPM_VER('${1}') -> '${s}'"
    echo "${s}"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function RPM_INSTALL () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local opts=""
    [ ${DEBUG} = "yes" ] && opts="-v"
    JOB_START "RPM Install"
    if [ -z "${1}" ] ; then
        ERROR "RPM_INSTALL(): Argument missing!"
    else
        LOG=""
        INFO "Perform RPM Install ${*}"
        __run_log "rpm ${opts} -Uh \"${1}\""
        _rc=$?
    fi
    JOB_END "RPM Install"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}

# We no longer work with python2, kept for legacy purposes
function _install_pip () {
    _install_pip3
}


function _install_pip3 () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _venv="${APP_VENV}"
    local _venv_path="${RSB_VENV}/${_venv}"
    JOB_START "Setup 'pip3' in venv: ${_venv_path}"
    while true ; do
        ${PKG_INSTALL} \
            "curl" \
            "python3" \
            "python3-dev" \
            "python3-venv" \
            "build-essential"\
          || break
        python3 -m venv "${_venv_path}" || break
        PIP3_INSTALL --upgrade "pip" || break
        PIP3_INSTALL --upgrade "setuptools" || break
        _rc=0
        break
    done
    JOB_END "Setup 'pip3' in venv: ${_venv_path}"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}

function _install_pipx () {
    local _rc=1
    local opts=""
    JOB_START "Install 'pipx'"
    while true ; do
        ${PKG_INSTALL} "pipx" "python3-argcomplete"
        pipx install pipx || break
        APT_REMOVE "pipx" || break
        /root/.local/bin/pipx install pipx --global || break
        /usr/local/bin/pipx uninstall pipx || break
        hash -r
        register-python-argcomplete pipx > /etc/bash_completion.d/pipx || break
        _rc=0
        break
    done
    JOB_END "Install 'pipx'"
    return ${_rc}
}

# Similar to above. Python2 stuff now serves Python3.
function PIP_INSTALL () {
    PIP3_INSTALL "$@"
}

function PIPX_INSTALL () {
    local _rc=1
    local opts=""
    [ "${DEBUG}" = "yes" ] && opts="-vvv"
    JOB_START "PIPX Install"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIPX_INSTALL(): Argument(s) missing!"
            break
        fi
        if ! which pipx > /dev/null 2>&1 ; then
            _install_pipx || break
        fi
        INFO "Perform PIPX Install: ${*}"
        __run_log "pipx install --global ${opts} ${*}"
        _rc=$?
        break
    done
    JOB_END "PIPX Install"
    return ${_rc}
}

function PIPX_UNINSTALL () {
    local _rc=1
    local opts=""
    [ "${DEBUG}" = "yes" ] && opts="-vvv"
    JOB_START "PIPX Uninstall"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIPX_UNINSTALL(): Argument(s) missing!"
            break
        fi
        if ! which pipx > /dev/null 2>&1 ; then
            _install_pipx || break
        fi
        INFO "Perform PIPX Install: ${*}"
        __run_log "pipx uninstall --global ${opts} ${*}"
        _rc=$?
        break
    done
    JOB_END "PIPX Uninstall"
    return ${_rc}
}

function PIP3_INSTALL () {
    local _rc=1
    local opts=""
    local _venv="${APP_VENV}"
    local _venv_path="${RSB_VENV}/${_venv}"
    [ ${DEBUG} = "yes" ] && opts="-vvv"
    JOB_START "PIP3 Install"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIP3_INSTALL(): Argument(s) missing!"
            break
        fi
        if [[ ! -f "${_venv_path}/bin/pip3" ]] ; then
            _install_pip3 || break
        fi
        INFO "Perform PIP3 Install: ${*} in venv: ${_venv_path}"
        __run_log "${_venv_path}/bin/pip3 install ${opts} ${*}"
        _rc=$?
        break
    done
    JOB_END "PIP3 Install"
    return ${_rc}
}


function PIP_UNINSTALL () {
    PIP3_UNINSTALL "$@"
}


function PIP3_UNINSTALL () {
    local _rc=1
    local opts=""
    local _venv="${APP_VENV}"
    local _venv_path="${RSB_VENV}/${_venv}"
    [ ${DEBUG} = "yes" ] && opts="-vvv"
    JOB_START "PIP3 Uninstall"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIP3_UNINSTALL(): Argument(s) missing!"
            break
        fi
        if [[ -x "${_venv_path}/bin/pip3" ]] ; then
            INFO "Using venv pip for uninstall: ${_venv_path}"
            __run_log "${_venv_path}/bin/pip3 uninstall ${opts} -y ${*}"
            _rc=$?
            break
        else
            ERROR "Virtual environment at ${_venv_path} does not have pip3, cannot uninstall: ${*}"
            break
        fi
    done
    JOB_END "PIP3 Uninstall"
    return ${_rc}
}


function PIP_CHECK () {
    PIP3_CHECK "$@"
}


function PIP3_CHECK () {
    local _rc=1
    local opts="-q"
    [ ${DEBUG} = "yes" ] && opts="-vvv"
    local _venv="${APP_VENV}"
    local _venv_path="${RSB_VENV}/${_venv}"
    JOB_START "PIP Check"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIP3_CHECK(): Argument(s) missing!"
            break
        fi
        if ! which pip3 > /dev/null 2>&1  ; then
            _install_pip3 || break
        fi
        if [ -x "${_venv_path}/bin/pip3" ] ; then
            INFO "Using venv pip for check: ${_venv_path}"
            if [ ${DEBUG} = "yes" ] ; then
                __run_log "${_venv_path}/bin/pip3 show ${opts} ${*}"
            else
                __run_log "${_venv_path}/bin/pip3 show ${opts} ${*} > /dev/null 2>&1"
            fi
            _rc=$?
            break
        else
            ERROR "Virtual environment at ${_venv_path} does not have pip3"
            break
        fi
    done
    JOB_END "PIP Check"
    return ${_rc}
}


function PIP_VER () {
    PIP3_VER "$@"
}


function PIP3_VER () {
    local _rc=1
    local s
    local _venv="${APP_VENV}"
    local _venv_path="${RSB_VENV}/${_venv}"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIP3_VER(): Argument(s) missing!"
            break
        fi
        if ! which pip3 > /dev/null 2>&1  ; then
            _install_pip3 || break
        fi
        if [ -x "${_venv_path}/bin/pip3" ] ; then
            s=$( "${_venv_path}/bin/pip3" show "${1}" 2>/dev/null | grep -m1 -e '^Version:' | awk '{ print $2 }' )
            [ "${DEBUG}" = "yes" ] && DEBUG "PIP3_VER('${1}') venv:${_venv} -> '${s}'"
        else
            ERROR "Virtual environment at ${_venv_path} does not have pip3"
            break
        fi
        echo "${s}"
        _rc=0
        break
    done
    return ${_rc}
}

function PIPX_VER () {
    local _rc=1
    local s=""
    local _name
    local _json

    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "PIPX_VER(): Argument(s) missing!"
            break
        fi
        _name="${1}"

        if ! command -v pipx >/dev/null 2>&1 ; then
            _install_pipx || break
        fi

        if ! command -v jq >/dev/null 2>&1 ; then
            ${PKG_INSTALL} "jq" || break
        fi

        _json="$( pipx list --global --json 2>/dev/null )"
        [ -z "${_json}" ] && break

        s="$( echo "${_json}" \
          | jq -r --arg name "${_name}" \
          '.venvs[$name].metadata.main_package.package_version // empty' )"

        [ "${DEBUG}" = "yes" ] && DEBUG "PIPX_VER('${_name}') -> '${s}'"

        echo "${s}"
        _rc=0
        break
    done

    return ${_rc}
}



function MONIT_RELOAD () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    JOB_START "'monit' reload"
    ${__run_cmd} "service 'monit' status"
    if [ "$?" != "0" ] ; then
        ERROR "'monit' service is not active"
    else
        __run_log "monit reload"
        _rc=$?
    fi
    JOB_END "'monit' reload"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function RUN_LOG () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=1
    local _cmd
    JOB_START "Run command"
    if [ -z "${1}" ] ; then
        ERROR "RUN_LOG(): Argument missing!"
    else
        _cmd="${1}"
        __run_log "${_cmd}"
        _rc=$?
    fi
    JOB_END "Run command"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function AUTOCONF_UPDATE () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _autoconf_src_path
    local _oWD
    local _rc=1

    JOB_START "Autoconf Update"
    _oWD=$( pwd )
    while true ; do
        WGET "http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.gz" || break
        TAR_ZXF "${TMP_DIR}/autoconf-latest.tar.gz" "${TMP_DIR}/" || break
        _autoconf_src_path="$( find "${TMP_DIR}/" -maxdepth 1 -type d -name "auto*" 2> /dev/null )"
        [ -z "${_autoconf_src_path}" ] && break
        [ ! -d "${_autoconf_src_path}" ] && break
        cd "${_autoconf_src_path}"
        RUN_LOG "./configure" || break
        RUN_LOG "make" || break
        RUN_LOG "make install" || break
        _rc=0
        break
    done
    cd "${_oWD}"

    JOB_END "Autoconf Update"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function FW_OPEN_PORTS () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _main_rc=1
    local _rc
    local firewalld="no"
    local iptables="no"
    local port
    local proto
    local p

    JOB_START "Firewall: open ports"

    if [ -z "${*}" ] ; then
        ERROR "FW_OPEN_PORTS(): Argument missing!"
        _main_rc=1
    else
        RUN_LOG "service firewalld status > /dev/null 2>&1" && firewalld="yes"
        RUN_LOG "service iptables status > /dev/null 2>&1" && iptables="yes"
        _main_rc=0
        if [ "${firewalld}" = "yes" -o "${iptables}" = "yes" ] ; then
            for p in "${@}" ; do
                INFO "Opening port: '${p}'"
                port="$(  echo "${p}" | awk -F'/' '{print $1}' )"
                proto="$( echo "${p}" | awk -F'/' '{print $2}' )"
                _rc=1
                if [ "${firewalld}" = "yes" ] ; then
                    RUN_LOG "firewall-cmd --zone=public --add-port=${port}/${proto} --permanent"
                elif [ "${iptables}" = "yes" ] ; then
                    RUN_LOG "iptables -I INPUT -p ${proto} -m ${proto} --dport ${port} -j ACCEPT"
                    RUN_LOG "iptables -D INPUT -j REJECT --reject-with icmp-host-prohibited" \
                        && RUN_LOG "iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited"
                    RUN_LOG "iptables -D INPUT -j LOGGING" \
                        && RUN_LOG "iptables -A INPUT -j LOGGING"
                fi
            done
            [ "${firewalld}" = "yes" ] && RUN_LOG "firewall-cmd --reload"
            [ "${iptables}" = "yes" ] && RUN_LOG "service iptables save"
        else
            WARN "Neither FirewallD nor IPTables services are active. Skipping..."
            _main_rc=1
        fi
    fi
    JOB_END "Firewall: open ports"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_main_rc}
}


function FW_REMOVE_PORTS () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _main_rc=1
    local _rc
    local firewalld="no"
    local iptables="no"
    local port proto p

    JOB_START "Firewall: remove ports"

    if [ -z "${*}" ] ; then
        ERROR "FW_REMOVE_PORTS(): Argument missing!"
        _main_rc=1
    else
        RUN_LOG "service firewalld status > /dev/null 2>&1" && firewalld="yes"
        RUN_LOG "service iptables status > /dev/null 2>&1" && iptables="yes"
        _main_rc=0
        if [ "${firewalld}" = "yes" -o "${iptables}" = "yes" ] ; then
            for p in "${@}" ; do
                INFO "Removing port: '${p}'"
                port="$(  echo "${p}" | awk -F'/' '{print $1}' )"
                proto="$( echo "${p}" | awk -F'/' '{print $2}' )"
                _rc=1
                if [ "${firewalld}" = "yes" ] ; then
                    RUN_LOG "firewall-cmd --zone=public --remove-port=${port}/${proto} --permanent"
                elif [ "${iptables}" = "yes" ] ; then
                    RUN_LOG "iptables -D INPUT -p ${proto} -m ${proto} --dport ${port} -j ACCEPT"
                fi
            done
            [ "${firewalld}" = "yes" ] && RUN_LOG "firewall-cmd --reload"
            [ "${iptables}" = "yes" ] && RUN_LOG "service iptables save"
        else
            WARN "Neither FirewallD nor IPTables services are active. Skipping..."
            _main_rc=1
        fi
    fi
    JOB_END "Firewall: remove ports"
    return ${_main_rc}
}


# --
# https://github.com/nodesource/distributions
# --
function NODEJS_INSTALL () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _rc=0
    local NODE_MAJOR
    local __ver __ver_major
    #
    case "${LSB_VENDOR}" in
      "Debian"|"Ubuntu")
          case "${LSB_VER}" in
            "Debian8")
                NODE_MAJOR="16"
                ;;
            "Debian9")
                NODE_MAJOR="16"
                ;;
            "Debian10")
                NODE_MAJOR="18"
                ;;
            "Ubuntu16.04")
                NODE_MAJOR="16"
                ;;
            "Ubuntu18.04")
                NODE_MAJOR="18"
                ;;
            "Ubuntu20.04")
                NODE_MAJOR="18"
                ;;
            "Ubuntu22.04")
                NODE_MAJOR="18"
                ;;
            "Ubuntu24.04")
                NODE_MAJOR="22"
                ;;
            *)
                ERROR "NODEJS_INSTALL(): ${LSB_VENDOR} is not supported!"
                return 1
                ;;
          esac
          if which "npm" > /dev/null 2>&1 ; then
              if [ -e "/etc/apt/sources.list.d/nodesource.list" ] ; then
                  if grep -q " nodistro " "/etc/apt/sources.list.d/nodesource.list" > /dev/null 2>&1 ; then
                      __ver="$( nodejs --version 2> /dev/null )"
                      __ver_major="$( \
                           awk -F '.' '{print $1}' 2> /dev/null <<< "${__ver}" \
                               | tr -d 'v' )"
                      if [ "${__ver_major}" = "${NODE_MAJOR}" ] ; then
                          INFO "NODEJS_INSTALL(): NodeJS ${__ver} is already installed"
                          return 0
                      fi
                  else
                      INFO "NODEJS_INSTALL(): NodeJS ${__ver} is installed: need to update package repositories"
                  fi
              else
                  INFO "NODEJS_INSTALL(): NodeJS ${__ver} is installed: need to install new package repositories"
              fi
          fi
          APT_INSTALL "ca-certificates" "curl" "gnupg" || return $?
          if [ ! -d "/etc/apt/keyrings" ] ; then
              MKDIR "/etc/apt/keyrings" || return $?
          fi
          RUN_LOG "curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg" || return $?
          echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" > "/etc/apt/sources.list.d/nodesource.list"
          APT_UPDATE || return $?
          APT_INSTALL "nodejs" || return $?
          _rc=0
          ;;
      *)
          return 0
          ;;
    esac
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function a2ensite_centos () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _site
    local _rc=1
    JOB_START "Enable Apache2 site (${LSB_VENDOR})"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "a2ensite_centos(): Argument missing!"
            break
        else
            _site="${1}"
            INFO "Enabling site: [${_site}]"
            if [ ! -e "/etc/apache2/sites-available/${_site}" ] ; then
                if [ -e "/etc/apache2/sites-available/${_site}.conf" ] ; then
                    _site="${_site}.conf"
                else
                    ERROR "${_site}: site not found"
                    break
                fi
            fi
            LN "/etc/apache2/sites-available/${_site}" \
               "/etc/apache2/sites-enabled/${_site}"
            _rc=$?
        fi
        break
    done
    JOB_END "Enable Apache2 site (${LSB_VENDOR})"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function a2dissite_centos () {
#    [ "${DEBUG}" = "yes" ] && ENTRY
    local _site
    local _rc=1
    JOB_START "Disable Apache2 site (${LSB_VENDOR})"
    while true ; do
        if [ -z "${1}" ] ; then
            ERROR "a2dissite_centos(): Argument missing!"
            break
        else
            _site="${1}"
            INFO "Disabling site: [${_site}]"
            if [ ! -e "/etc/apache2/sites-enabled/${_site}" ] ; then
                if [ -e "/etc/apache2/sites-enabled/${_site}.conf" ] ; then
                    _site="${_site}.conf"
                else
                    ERROR "${_site}: site not found"
                    break
                fi
            fi
            RM_FILES "/etc/apache2/sites-enabled/${_site}"
            _rc=$?
        fi
        break
    done
    JOB_END "Disable Apache2 site (${LSB_VENDOR})"
#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


# ----------------------------------------------------------
# Prepare environment functions
# ----------------------------------------------------------

function _prep_env () {
    local d
    local __pkg
    local __s __systemd_reload

#    JOB_START "Prepare environment"

    for d in \
      "Documents" \
      "Downloads" \
      "Torrents" \
      "Watch" \
      "tmp" \
      "log"
    do
        [ ! -d "/etc/skel/${d}" ] && MKDIR "/etc/skel/${d}"
        [ ! -d "${USERHOME}/${d}" ] && MKDIR "${USERHOME}/${d}" && CHOWN_DIR "${USERNAME}:${APACHE_GROUP}" "${USERHOME}/${d}"
    done

    # Configure `logrotate`
    if [ -d "/etc/logrotate.d" ] ; then
 #       INFO "Configuring 'logrotate'"
        cat <<-EOF > "/etc/logrotate.d/install-app"
	/var/log/install-app.log
	{
	        rotate 4
	        weekly
	        missingok
	        notifempty
	        compress
	        delaycompress
	}

	/var/log/whmcs_client_installers/*.log
	{
	        rotate 4
	        weekly
	        missingok
	        notifempty
	        compress
	        delaycompress
	}
	EOF
        cat <<-EOF > "/etc/logrotate.d/seedbox-cp"
	/var/log/seedbox-cp.log
	{
	        rotate 4
	        weekly
	        missingok
	        notifempty
	        compress
	        delaycompress
	}
	EOF
        cat <<-EOF > "/etc/logrotate.d/rsb-logs"
	/var/log/rsb/*.log
	{
	        rotate 4
	        weekly
	        missingok
	        notifempty
	        compress
	        delaycompress
	}
	EOF
        cat <<-EOF > "/etc/logrotate.d/user-logs"
	/home/*/log/*.log
	{
	        rotate 4
	        weekly
	        missingok
	        notifempty
	        compress
	        delaycompress
	}
	EOF
    fi

    # Remove obsolete scripts && unnecessary stuff
    #    INFO "Removing obsolete stuff"
    [ -d "/opt/install_scripts" ] && RM_DIRS "/opt/install_scripts"
    [ -e "/etc/apt/sources.list.d/bunkus.org.list" ] \
        && RM_FILES "/etc/apt/sources.list.d/bunkus.org.list"
    [ -e "/etc/apt/sources.list.d/getdeb.list" ] \
        && MV "/etc/apt/sources.list.d/getdeb.list" "/etc/apt/sources.list.d/getdeb.list.BAK"

    # Initialize global variables
    #    INFO "Initialize global variables"
    FIRST_UID="$( grep -e '^UID_MIN\s*' "/etc/login.defs" | awk '{print $2}' )"

    # Add symlink to user's Downloads in /media directory
    [ ! -d "/media" ] && MKDIR "/media"
    if [ ! -L "/media/Downloads" ] ; then
        RUN_LOG "ln -svf ${USERHOME}/Downloads/. /media/Downloads 2>&1"
    elif [ "$( readlink -f "/media/Downloads" )" != "/home/user/Downloads" ] ; then
        RUN_LOG "ln -svf ${USERHOME}/Downloads/. /media/Downloads 2>&1"
    fi

    # Set some packages on hold:
    if grep -qE '^.*-16\.04-3.-.*' "/rsb-template.id" 2> /dev/null ; then
        if which apt-mark > /dev/null 2>&1 ; then
            for __pkg in \
              "systemd" \
              "systemd-coredump" \
              "systemd-sysv" \
              "libpam-systemd" \
            ; do
                    if ! apt-mark showhold  | grep -qE "^${__pkg}$" ; then
                        INFO "${__pkg}: set hold"
                        RUN_LOG "apt-mark hold ${__pkg}"
#                        # Don't hold `systemd`: it seems they fixed the packages :)
#                        # https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1804847/comments/17
#                        INFO "${__pkg}: set hold"
#                        RUN_LOG "apt-mark hold ${__pkg}"
#                    else
#                        INFO "${__pkg}: already set hold"
                    fi
            done
        else
            INFO "apt-mark: command not found."
        fi
    fi

    #
    # rsb.init.d mandatory(!) scripts
    #
    [ -d "/etc/rsb.init.d" ] || MKDIR "/etc/rsb.init.d"

    # -- rsb.init.d scripts default logging directory
    [ -d "/var/log/rsb" ] || MKDIR "/var/log/rsb"

    # -- Remove /core.* garbage on startup
    cat <<-EOF > "/etc/rsb.init.d/001.rm-cores.sh"
	#!/bin/bash

	[ -d "/var/log/rsb" ] || mkdir -p "/var/log/rsb"
	LOGFILE="/var/log/rsb/001.rm-cores.log"
	. "/usr/local/lib/rsb/functions.sh"

	# Remove garbage: /core.*

	SCRIPTENTRY

	RUN_LOG "rm -f /core.*"

	SCRIPTEXIT
	EOF

    # -- Fix filesystem permissions
    if [ -n "${APACHE_USER}" -a -n "${APACHE_GROUP}" ] ; then
        if id "${APACHE_USER}" > /dev/null 2>&1 ; then
#            RUN_LOG "find /home -wholename '/home/*/Downloads' \
#                                -exec setfacl -Rm g:${APACHE_GROUP}:rwX,d:g:${APACHE_GROUP}:rwX {} \;"
#            ${__run_cmd} "find /home -wholename '/home/*/Downloads' \
#                                -exec setfacl -Rm g:${APACHE_GROUP}:rwX,d:g:${APACHE_GROUP}:rwX {} \;"
            ${__run_cmd} "timeout -k 9 300 \
                              find /home \
                                  -wholename '/home/*/Downloads' \
                                  -exec setfacl -Rm g:${APACHE_GROUP}:rwX,d:g:${APACHE_GROUP}:rwX {} \;"
        fi
    fi

    # -- Setup `systemd` accounting
    if [ -e "/etc/systemd/system.conf" ] ; then
        __systemd_reload="no"
        for __s in \
            "DefaultCPUAccounting"     \
            "DefaultIOAccounting"      \
            "DefaultIPAccounting"      \
            "DefaultBlockIOAccounting" \
            "DefaultMemoryAccounting"  \
            "DefaultTasksAccounting"   \
        ; do
            case "${LSB_VER}" in
              "Ubuntu16.04")
                  case "${__s}" in
                      "DefaultCPUAccounting"     \
                    | "DefaultBlockIOAccounting" \
                    | "DefaultMemoryAccounting"  \
                    | "DefaultTasksAccounting"   \
                    )
                        ;;
                    *)
                        continue
                        ;;
                  esac
                  ;;
            esac
            if [ "${__s}" = "DefaultIPAccounting" ] ; then
                [ "$( systemd-detect-virt )" = "openvz" ] && continue
            fi
            if ! grep -q -E "^${__s}=" "/etc/systemd/system.conf" 2> /dev/null ; then
                INFO "/etc/systemd/system.conf: Setting ${__s}=yes"
                echo "${__s}=yes" >> "/etc/systemd/system.conf"
                __systemd_reload="yes"
            fi
        done

        if [ ${__systemd_reload} = "yes" ] ; then
            SYSTEMD_RELOAD
#            RUN_LOG "touch /var/run/reboot-required"
        fi
    fi


#    JOB_END "Prepare environment"
}


function _create_std_user () {
    local _uid
    local _g
    local _rc
    local _main_rc=0

#    JOB_START "Check standard user account(s)"

    _uid="$( id -u "user" 2>/dev/null )"
    if [ ! -z "${_uid}" ] ; then
        INFO "'user': account exists!"
    else
        # Add regular user's account
        case "${LSB_VENDOR}" in
          "CentOS"|"CentOSStream")
              RUN_LOG "adduser -c 'RapidSeedbox.com User' -m user"
              _rc=$?
              CHMOD_FILE 0755 "/home/user"
              ;;
          "Debian"|"Devuan"|"Ubuntu")
              RUN_LOG "adduser --gecos 'RapidSeedbox.com User' --disabled-password user"
              _rc=$?
              CHMOD_FILE 0755 "/home/user"
              ;;
          *)
              _rc=1
              ;;
        esac
        if [ ${_rc} -ne 0 ] ; then
            _main_rc="${_rc}"
        else
            # set user's password
            if which base64 > /dev/null 2>&1 ; then
                # Set 'pseudo-random' password
                RUN_LOG "echo 'user:$( echo "${RANDOM}" | base64 )' | chpasswd"
            else
                # Set 'standard' password
                RUN_LOG "echo 'user:fiUthudfac4' | chpasswd"
            fi
            _rc=$?
            [ ${_rc} -ne 0 ] && _main_rc=${_rc}
            # Modify new user's group membership
            for _g in sudo ${APACHE_GROUP} ; do
                if grep -e "^${_g}:" "/etc/group" > /dev/null 2>&1 ; then
                    RUN_LOG "usermod -a -G ${_g} user"
                    _rc=$?
                    [ ${_rc} -ne 0 ] && _main_rc=${_rc}
                fi
            done
        fi
    fi
#    JOB_END "Check standard user account(s)"
    return ${_main_rc}
}


function _prep_ssh () {

    local sshd_restart="no"
    local f

#    JOB_START "Configure SSHd"

    # Configure sshd
    for f in \
        "users-rapidseedbox-com-ca.pub" \
        "revoked_keys"
    do
        if [ ! -f "/etc/ssh/${f}" ] ; then
            WGET "${SUPPORT}/ssh/${f}" \
                 "/etc/ssh/${f}" \
              || return 1
            if [ ! -s "/etc/ssh/${f}" ] && [ "${f}" != "revoked_keys" ] ; then
                 ERROR "'/etc/ssh/${f}': Empty file downloaded!"
                 RM_FILES "/etc/ssh/${f}"
                 return 1
            fi
        fi
    done

    # set TrustedUserCAKeys
    ${__run_cmd} "grep -e '^TrustedUserCAKeys' '/etc/ssh/sshd_config'"
    if [ $? -eq 0 ] ; then
        ${__run_cmd} "grep -e '^TrustedUserCAKeys /etc/ssh/users-rapidseedbox-com-ca.pub' '/etc/ssh/sshd_config'"
        if [ $? -ne 0 ] ; then
            RUN_LOG "sed -i -e 's#^TrustedUserCAKeys.*#TrustedUserCAKeys /etc/ssh/users-rapidseedbox-com-ca.pub#g' '/etc/ssh/sshd_config'"
            [ $? -eq 0 ] && sshd_restart="yes"
        fi
    else
        RUN_LOG "echo 'TrustedUserCAKeys /etc/ssh/users-rapidseedbox-com-ca.pub' >> '/etc/ssh/sshd_config'"
        [ $? -eq 0 ] && sshd_restart="yes"
    fi

    # set RevokedKeys
    ${__run_cmd} "grep -e '^RevokedKeys' '/etc/ssh/sshd_config'"
    if [ $? -eq 0 ] ; then
        ${__run_cmd} "grep -e '^RevokedKeys /etc/ssh/revoked_keys' '/etc/ssh/sshd_config'"
        if [ $? -ne 0 ] ; then
            RUN_LOG "sed -i -e 's#^RevokedKeys.*#RevokedKeys /etc/ssh/revoked_keys#g' '/etc/ssh/sshd_config'"
            [ $? -eq 0 ] && sshd_restart="yes"
        fi
    else
        RUN_LOG "echo 'RevokedKeys /etc/ssh/revoked_keys' >> '/etc/ssh/sshd_config'"
        [ $? -eq 0 ] && sshd_restart="yes"
    fi

    [ "${sshd_restart}" = "yes" ] && SVC_RESTART "${SSHD_SVC}"

#    JOB_END "Configure SSHd"
    return 0
}


function _prep_apache2_centos() {

    local _rc=1

#    JOB_START "Configure Apache2 (${LSB_VENDOR})"

    ${PKG_CHECK} "${APACHE_SVC}"
    if [ $? -ne 0 ] ; then
        JOB_START "Configure Apache2 (${LSB_VENDOR})"
        if ${PKG_INSTALL} "${APACHE_SVC}" ; then
            # Disable default welcome page:
            RUN_LOG "sed -i -e 's/^\(.*\)$/#\1/g' '/etc/httpd/conf.d/welcome.conf' 2>&1"
            # Make Debian-style config directory structure
            # -- common settings
            LN "/etc/httpd" "/etc/apache2"
            LN "/etc/httpd/conf/httpd.conf" "/etc/httpd/apache2.conf"
            # -- configurations
            MKDIR "/etc/httpd/conf-available"
            LN "/etc/httpd/conf.d" "/etc/httpd/conf-enabled"
            # -- modules
            LN "/etc/httpd/modules" "/etc/httpd/mods-available"
            if [ -d "/etc/httpd/conf.modules.d" ] ; then
                LN "/etc/httpd/conf.modules.d" "/etc/httpd/mods-enabled"
            else
                LN "/etc/httpd/modules" "/etc/httpd/mods-enabled"
            fi
            # -- sites
            MKDIR "/etc/httpd/sites-available"
            LN "/etc/httpd/conf.d" "/etc/httpd/sites-enabled"
            # -- passwords
            MKDIR "/etc/httpd/passwords-available"
            MKDIR "/etc/httpd/passwords-enabled"
            #
            # Install SSL module
            ${PKG_INSTALL} "mod_ssl"
            #
            # Symlink RSB certificate
            LN "rsb.pem" "/etc/ssl/private/apache2.pem"
            # Enable RSB certificate in Apache config
            RUN_LOG "sed -i \
                         -e 's|^SSLCertificateFile\s*.*$|SSLCertificateFile /etc/ssl/private/apache2.pem|g' \
                         '/etc/httpd/conf-enabled/ssl.conf' 2>&1"
            RUN_LOG \
               "sed -i \
                    -e 's|^SSLCertificateKeyFile\s*.*$|SSLCertificateKeyFile /etc/ssl/private/apache2.pem|g' \
                         '/etc/httpd/conf-enabled/ssl.conf' 2>&1"
            #
            # Configure firewall
            case "${LSB_RELEASE}" in
              "6")
                  RUN_LOG "iptables -I INPUT -p tcp -m tcp --dport 443 -j ACCEPT"
                  RUN_LOG "service iptables save"
                  ;;
              "7"|"8")
                  RUN_LOG "systemctl is-active firewalld 2>&1"
                  if [ $? -eq 0 ] ; then
                      RUN_LOG "firewall-cmd --zone=public --add-port=443/tcp --permanent"
                      RUN_LOG "firewall-cmd --reload"
                  fi
                  RUN_LOG "systemctl is-active iptables 2>&1"
                  if [ $? -eq 0 ] ; then
                      RUN_LOG "iptables -I INPUT -p tcp -m tcp --dport 443 -j ACCEPT"
                      RUN_LOG "service iptables save"
                  fi
                  ;;
            esac
            #
            # Enable/Start service
            SVC_ENABLE "${APACHE_SVC}"
            SVC_RESTART "${APACHE_SVC}"
            _rc=0
        fi
        JOB_END "Configure Apache2 (${LSB_VENDOR})"
    fi

    [ ! -L "/etc/apache2" ] && LN "/etc/httpd" "/etc/apache2"

#    JOB_END "Configure Apache2 (${LSB_VENDOR})"
    return ${_rc}
}


function _prep_apache2_deb() {

    local _rc=1
    local _apache_restart="no"
    local _apache_logfile="/dev/null"
    local _monit_reconfigure="no"

#    JOB_START "Configure Apache2 (${LSB_VENDOR})"

    DPKG_CHECK "${APACHE_SVC}"
    if [ "$?" != "0" ] ; then
        JOB_START "Configure Apache2 (${LSB_VENDOR})"
        APT_INSTALL "${APACHE_SVC}" \
                    "apache2-utils" \
            || return 1
        MKDIR "${APACHE_CONF_DIR}/userconf-enabled" || return 1
        # User access file
        cat <<-EOF > "/etc/skel/.htaccess" 2>> "${LOGFILE}"
	AuthType Digest
	AuthName "apache2"
	AuthUserFile ${APACHE_CONF_DIR}/users.password
	Require user ###USERNAME###
	EOF
        CHMOD_FILE 640 "/etc/skel/.htaccess"

        if [ -d "/etc/monit/conf.d" ] ; then
            cat <<-EOF > "/etc/monit/conf.d/apache2" 2>> "${LOGFILE}"
		check process apache2 with pidfile /var/run/apache2/apache2.pid
		   group apache
		   start program = "/bin/systemctl restart apache2"
		   stop program  = "/bin/systemctl stop apache2"
		#   if 5 restarts within 20 cycles then timeout
		   depend apache_bin
		   depend apache_rc

		check file apache_bin with path /usr/sbin/apache2
		   group apache
		   include /etc/monit/templates/rootbin

		check file apache_rc with path /etc/init.d/apache2
		   group apache
		   include /etc/monit/templates/rootbin
		EOF
        fi

        if [ -e "/etc/fail2ban/jail.d" ] ; then
            cat <<-EOF > "/etc/fail2ban/jail.d/apache.conf" 2>> "${LOGFILE}"
		[apache-auth]
		enabled = true

		[apache-badbots]
		enabled = true

		[apache-noscript]
		enabled = true

		[apache-overflows]
		enabled = true

		[apache-nohome]
		enabled = true

		[apache-botsearch]
		enabled = true

		[apache-fakegooglebot]
		enabled = true

		[apache-modsecurity]
		enabled = true

		[apache-shellshock]
		enabled = true
		EOF
        fi

        # Enable SSL module
        RUN_LOG "a2enmod ssl"
        # Disable default site configuration
        RUN_LOG "a2dissite 000-default"
        # Define server name
        echo -ne "ServerName $( hostname )" > "${APACHE_CONF_DIR}/conf-available/fqdn.conf"
        RUN_LOG "a2enconf fqdn.conf"
        # RSB site configuration
        RUN_LOG "a2enmod userdir"
        RUN_LOG "a2enmod proxy"
        RUN_LOG "a2enmod proxy_http"
        RUN_LOG "a2enmod headers"
        RUN_LOG "a2enmod rewrite"
        RUN_LOG "a2enmod auth_digest"
        RUN_LOG "a2ensite rsb.conf"
        # Symlink RSB certificate
        RUN_LOG "ln -fs rsb.pem /etc/ssl/private/apache2.pem"

        # Generate selfsigned certificate
        if [ ! -e "/etc/ssl/private/rsb.pem" ] ; then
            INFO "Creating new self-signed certificate"
            RUN_LOG "make-ssl-cert generate-default-snakeoil --force-overwrite 2>&1"
            RUN_LOG "cat '/etc/ssl/certs/ssl-cert-snakeoil.pem' \
                         '/etc/ssl/private/ssl-cert-snakeoil.key' \
                       > '/etc/ssl/private/rsb.pem'"
            CHMOD_FILE 0600 "/etc/ssl/private/rsb.pem"
            RM_FILES "/etc/ssl/certs/ssl-cert-snakeoil.pem" \
                     "/etc/ssl/private/ssl-cert-snakeoil.key"
            RUN_LOG "find /etc/ssl/certs -lname ssl-cert-snakeoil.pem -exec rm -v {} \; 2>&1"
        fi

        _apache_restart="yes"

        _rc=0

        JOB_END "Configure Apache2 (${LSB_VENDOR})"

    fi

    if [ -e "${APACHE_CONF_DIR}/ports.conf" ] ; then
        if grep -q -e '^\s*Listen 80\s*$' "${APACHE_CONF_DIR}/ports.conf" 2> /dev/null ; then
            INFO "Disable 'Listen 80' directive"
            RUN_LOG "sed -i -e 's|^\s*\(Listen 80\)\s*$|#\1|' ${APACHE_CONF_DIR}/ports.conf"
            _apache_restart="yes"
        fi
    fi



    # Removing 'mod_security2' module as it is problematic sometimes
    if [[ -e "${APACHE_CONF_DIR}/conf-enabled/server-security.conf" ]] \
    && [[ ! -e "/var/local/.mod_security2_removed" ]] ; then
      RUN_LOG "a2disconf server-security.conf"
      RM_FILES "${APACHE_CONF_DIR}/conf-available/server-security.conf"
      if [[ ! -e "${APACHE_CONF_DIR}/sites-available/rsbssl_easy_${SUBDOMAIN}-dav.conf" ]]; then
        RUN_LOG "a2dismod security2"
      fi
      RUN_LOG "touch /var/local/.mod_security2_removed"
      _apache_restart="yes"
    fi

    [ -e "/var/local/.rsb-p3a-client" ] \
        && _apache_logfile="/run/rsb-p3a-access.log"

    cat <<-EOF > "${TMP_DIR}/rsb.conf" 2>> "${LOGFILE}"
	# ------------------------------------------------------------ #
	#                                                              #
	# !!! PLEASE DON'T CHANGE THIS FILE !!!                        #
	#                                                              #
	# -- Your RSB team :)                                          #
	#                                                              #
	# ------------------------------------------------------------ #
	<VirtualHost _default_:443>
	    ServerAdmin webmaster@localhost
	    DocumentRoot /var/www/html
	    ErrorLog \${APACHE_LOG_DIR}/error.log
	    #CustomLog \${APACHE_LOG_DIR}/access.log combined
	    CustomLog ${_apache_logfile} combined
	    SSLEngine on
	    SSLCertificateFile /etc/ssl/private/apache2.pem
	    SSLCertificateKeyFile /etc/ssl/private/apache2.pem
	    <FilesMatch "\.(cgi|shtml|phtml|php)$">
	        SSLOptions +StdEnvVars
	    </FilesMatch>
	    <Directory /usr/lib/cgi-bin>
	        SSLOptions +StdEnvVars
	    </Directory>

	    <IfModule mod_userdir.c>
	        UserDir /home/*
	        UserDir disabled root
	        HeaderName HEADER.html
	        ReadmeName README.html
	        <Directory /home/*>
	            AllowOverride FileInfo AuthConfig Limit Indexes
	            Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
	            <Limit GET POST OPTIONS>
	                Require all granted
	            </Limit>
	            <LimitExcept GET POST OPTIONS>
	                Require all denied
	            </LimitExcept>
	        </Directory>
	    </IfModule>

	    IncludeOptional userconf-enabled/*.conf

	</VirtualHost>
	EOF
    ${__run_cmd} "diff ${TMP_DIR}/rsb.conf ${APACHE_CONF_DIR}/sites-available/rsb.conf 2>&1"
    if [ $? -ne 0 ] ; then
        INFO "[${APACHE_CONF_DIR}/sites-available/rsb.conf]: Replacing with a newer version"
        MV "${TMP_DIR}/rsb.conf" "${APACHE_CONF_DIR}/sites-available/rsb.conf"
        ${A2ENSITE} "rsb.conf"
        _apache_restart="yes"
    fi
    #
    if [ -e "${APACHE_CONF_DIR}/sites-enabled/rutorrent-8080.conf" ] ; then
        if [ "${USERUID}" = "${FIRST_UID}" ] ; then
            cat <<-EOF > "${TMP_DIR}/rutorrent-${USERNAME}.conf"
		ProxyRequests off
		ProxyPreserveHost On

		RewriteEngine on
		RewriteRule ^/rutorrent$ /rutorrent/ [R=permanent]
		RewriteRule ^/rutorrent-${USERNAME}$ /rutorrent-${USERNAME}/ [R=permanent]

		<Location "/rutorrent/">
		    Options +Indexes +Includes +FollowSymLinks +MultiViews
		    AllowOverride None
		    AuthType Basic
		    AuthName "ruTorrent"
		    AuthBasicProvider file
		    AuthUserFile  "/var/www/rutorrent/.htpasswd"
		    Require user ${USERNAME}
		    ProxyPass http://127.0.0.1:8080/
		    ProxyPassReverse http://127.0.0.1:8080/
		</Location>

		<Location "/rutorrent-${USERNAME}/">
		    Options +Indexes +Includes +FollowSymLinks +MultiViews
		    AllowOverride None
		    AuthType Basic
		    AuthName "ruTorrent"
		    AuthBasicProvider file
		    AuthUserFile  "/var/www/rutorrent/.htpasswd"
		    Require user ${USERNAME}
		    ProxyPass http://127.0.0.1:8080/
		    ProxyPassReverse http://127.0.0.1:8080/
		</Location>
		EOF
        else
            cat <<-EOF > "${TMP_DIR}/rutorrent-${USERNAME}.conf"
		ProxyRequests off
		ProxyPreserveHost On

		RewriteEngine on
		RewriteRule ^/rutorrent-${USERNAME}$ /rutorrent-${USERNAME}/ [R=permanent]

		<Location "/rutorrent-${USERNAME}/">
		    Options +Indexes +Includes +FollowSymLinks +MultiViews
		    AllowOverride None
		    AuthType Basic
		    AuthName "ruTorrent"
		    AuthBasicProvider file
		    AuthUserFile  "/var/www/rutorrent/.htpasswd"
		    Require user ${USERNAME}
		    ProxyPass http://127.0.0.1:8080/
		    ProxyPassReverse http://127.0.0.1:8080/
		</Location>
		EOF
        fi
        if [ -e "${APACHE_CONF_DIR}/userconf-enabled/rutorrent-${USERNAME}.conf" ] ; then
            ${__run_cmd} "diff ${TMP_DIR}/rutorrent-${USERNAME}.conf ${APACHE_CONF_DIR}/userconf-enabled/rutorrent-${USERNAME}.conf"
            if [ $? -ne 0 ] ; then
                INFO "[${APACHE_CONF_DIR}/userconf-enabled/rutorrent-${USERNAME}.conf]: Replacing with a newer version"
                MV "${TMP_DIR}/rutorrent-${USERNAME}.conf" \
                   "${APACHE_CONF_DIR}/userconf-enabled/rutorrent-${USERNAME}.conf"
                _apache_restart="yes"
            fi
        else
            INFO "[${APACHE_CONF_DIR}/userconf-enabled/rutorrent-${USERNAME}.conf]: Installing new configuration"
            MV "${TMP_DIR}/rutorrent-${USERNAME}.conf" \
               "${APACHE_CONF_DIR}/userconf-enabled/rutorrent-${USERNAME}.conf"
            _apache_restart="yes"
        fi
    fi

    if [ "${_apache_restart}" = "yes" ] ; then
        # Restart service
        SVC_RESTART "${APACHE_SVC}"
    fi

    # -- Configure `monit` -- #
    if [ -d "/etc/monit/conf.d" ] ; then
        _monit_reconfigure="no"
        if [ ! -e "/etc/monit/conf.d/apache2" ] ; then
            _monit_reconfigure="yes"
        else
            if [ "$( head -1 "/etc/monit/conf.d/apache2" | awk '{print $NF}' )" != "1.1" ] ; then
                _monit_reconfigure="yes"
            fi
        fi
        if [ "${_monit_reconfigure}" = "yes" ] ; then
            cat <<-EOF > "/etc/monit/conf.d/apache2"
		# Version: 1.1
		check process apache2 with pidfile /var/run/apache2/apache2.pid
		   group apache
		   start program = "/usr/sbin/service apache2 restart &"
		   stop program  = "/usr/sbin/service apache2 stop &"
		   if 15 restarts within 200 cycles then timeout
		EOF
            if systemctl is-active "monit" > /dev/null 2>&1 ; then
                MONIT_RELOAD
                RUN_LOG "sleep 5"
                RUN_LOG "monit monitor apache2 2>&1"
            fi
        fi
    fi

#    JOB_END "Configure Apache2 (${LSB_VENDOR})"
    return ${_rc}
}


function _prep_openvpn () {
    local _os
    local -r OLD_CA="7C:88:FD:42:04:10:51:25:66:3E:BF:9A:50:3F:AC:F0:38:98:51:74:A1:9B:55:5A:BC:03:1D:D9:65:7C:B8:71"
    local -r NEW_CA="B9:F5:14:5C:00:4B:3E:3F:91:09:2C:D3:51:40:F6:55:0D:08:8F:F7:6A:E4:BE:2D:6B:93:DE:11:09:4A:61:34"
    local -r OLD_22_CA="9B:5A:A1:A6:EC:D7:5A:02:9A:CD:D7:A2:EA:B5:07:B7:37:84:C9:99:D7:E1:DA:CE:B2:3E:81:72:05:44:B0:A2"

    ${PKG_CHECK} "openvpn" || return

    local ca_fingerprint

    ca_fingerprint=$(openssl x509 -noout -fingerprint -sha256 -in /etc/openvpn/rsb-ca.crt | cut -d'=' -f2)
    if [[ $? -ne 0 || -z "$ca_fingerprint" ]]; then
        return
    fi

#    JOB_START "Configuring OpenVPN"

    for _os in "windows" "linux" "macosx" ; do
        [[ -e  "${USERHOME}/rapidseedbox-${_os}.ovpn" ]] && RM_FILES "${USERHOME}/rapidseedbox-${_os}.ovpn"
    done


    cat <<-EOF > "${USERHOME}/rapidseedbox.ovpn"
# Basic settings
dev tun
client
remote ${IP}
keepalive 10 120
persist-key
persist-tun
comp-lzo

# Client authentication
auth-user-pass
auth-nocache

# Server authentication
remote-cert-tls server
EOF
    case "${ca_fingerprint}" in
        "${OLD_22_CA}")
            cat <<-EOF >> "${USERHOME}/rapidseedbox.ovpn"
<ca>
-----BEGIN CERTIFICATE-----
MIIDTTCCAjWgAwIBAgIUZiGvBRS0T3rXqH2TzbR5LiwBtKowDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwIBcNMjIwNTExMTA0MDE1WhgPMjEy
MjA0MTcxMDQwMTVaMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuJb3M4VMM/ROETnQaqsdyG+wn+OPl/j3MFDY
cDAw/0N0lAUxexlK48bw5Q4sGYYDpZU7d/vVBBJERv+mNJqzjzrBuBx+fdStNuzd
oCWkKPKx8GC31eKl7AJ604kVHgTuirjsyA6dag0EFDw1JyPQxIXt3CUPr8yvKpee
Koj82Nv2isDdmwP8DVFxslSpXYPcLvzpnu+Py4j7xQvVjb8WztzbWnD6kduNzyt7
l7jivKcMihdg5iAYr+/IBhprMSMJ9VgJruQwFD8TAsgXSwaEXpTo1W69FqYJH8xw
6ddB1hgGQWo+gB/YCP8UvLEzoQ+K3wtc6kPfNXAc3IoeiFIglQIDAQABo4GQMIGN
MB0GA1UdDgQWBBQJeAgRDfOujTX6EKCi/JLlUBhK7TBRBgNVHSMESjBIgBQJeAgR
DfOujTX6EKCi/JLlUBhK7aEapBgwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0GCFGYh
rwUUtE9616h9k820eS4sAbSqMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0G
CSqGSIb3DQEBCwUAA4IBAQCndTpmUvpCLXW3REHGnmmWHROdSHvskTIyNVdOOvlT
98Smhj/IsLazDscPB2TjODyENdhOXE5NMBeceVXdn0V7x5cuopwzA/aZdioq8H8c
OslhlnBl3Cs82pY3SdIHmg7G6PS290eWpVky1c13BuRjCEdy1uaJ4JRxkz8YwHfR
C2vX7PhXnofT0C0tTjgzrcSXVUe2Ha3Uu8/2DGmEwVCQs6s5I4cqfGHBRH4v8EgY
l49WmkbRkGSqFqP1B/QDIXh7bvfmeQkpbmY98PnpDhR/+5PbdZfOKbuknVoB1NM3
1hSNdLMxdnjFaG1/VsZ2bQm6k1e13yq24pf5/9+4bqac
-----END CERTIFICATE-----
</ca>
EOF
            ;;
        "${OLD_CA}")
            cat <<-EOF >> "${USERHOME}/rapidseedbox.ovpn"
<ca>
-----BEGIN CERTIFICATE-----
MIIE9zCCA9+gAwIBAgIJALjpBRKWXpxvMA0GCSqGSIb3DQEBBQUAMIGtMQswCQYD
VQQGEwJGUjEMMAoGA1UECBMDUkJYMQwwCgYDVQQHEwNSQlgxGTAXBgNVBAoTEFJh
cGlkU2VlZGJveC5jb20xGTAXBgNVBAsTEHJhcGlkc2VlZGJveC5jb20xETAPBgNV
BAMTCHNlZWRib3gxMRAwDgYDVQQpEwdzZWVkYm94MScwJQYJKoZIhvcNAQkBFhhz
dXBwb3J0QHJhcGlkc2VlZGJveC5jb20wHhcNMTUwNzA3MjE0MzQxWhcNMjUwNzA0
MjE0MzQxWjCBrTELMAkGA1UEBhMCRlIxDDAKBgNVBAgTA1JCWDEMMAoGA1UEBxMD
UkJYMRkwFwYDVQQKExBSYXBpZFNlZWRib3guY29tMRkwFwYDVQQLExByYXBpZHNl
ZWRib3guY29tMREwDwYDVQQDEwhzZWVkYm94MTEQMA4GA1UEKRMHc2VlZGJveDEn
MCUGCSqGSIb3DQEJARYYc3VwcG9ydEByYXBpZHNlZWRib3guY29tMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoqrCAEeoILeEGThpRBIWhkPoN671Vz+m
sjesux5n+ig5FtsjfUPo2sZ2QPoPyLu26IlWHntqe4C4iHnrfklgq2Sn/+u+4v2b
C6uXAtUFY0qK2g+VG/q5OAq9Hh6cDwlM16G8eqDKJskS3GJIL4ZBwTlacIeWrXkr
v8bIDfODAlrpxy6A3kT+HZJ6ELwD/z/RAkoTOwew1RHFTtFn4Kesvw12+cIs+BSo
tgSK+74Sa1L4HDtDV71tQr+FNs0GKW0zv46KDZQRzEN+F5M35W8GJrOpLviIVciR
MgTENPcFl/oiaZAnslZb/kNbaEqB3ymtvPQ00s+aVbdBL+tB9sbbUQIDAQABo4IB
FjCCARIwHQYDVR0OBBYEFElnnCKkn+1B26Hdb7oGNs93Xp4iMIHiBgNVHSMEgdow
gdeAFElnnCKkn+1B26Hdb7oGNs93Xp4ioYGzpIGwMIGtMQswCQYDVQQGEwJGUjEM
MAoGA1UECBMDUkJYMQwwCgYDVQQHEwNSQlgxGTAXBgNVBAoTEFJhcGlkU2VlZGJv
eC5jb20xGTAXBgNVBAsTEHJhcGlkc2VlZGJveC5jb20xETAPBgNVBAMTCHNlZWRi
b3gxMRAwDgYDVQQpEwdzZWVkYm94MScwJQYJKoZIhvcNAQkBFhhzdXBwb3J0QHJh
cGlkc2VlZGJveC5jb22CCQC46QUSll6cbzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBBQUAA4IBAQAkxPGmWeI1Fu1k09lroAAUGwcn8a+om145mbn7NrNujiLjx6x9
t1Fqh77IbHl6hg0YrnTqFGknYJefolxbA75pvfpp+jUy+DJU0U+FfF5wM/2hdFvB
H8dkgJk6dzWWX3vzymfg/njHzAB2MioNfPt0CWHjRLKY7kkLJBcYDUDxZjr4VR7Z
HbuEv5ucQxbsNG1IsakzPJOC4ev+4YevOpzXpIEXjltwGkrDz2C3/b8Gh4Conbvn
n8xhRWqHyJqnzWSUGezZfp/CCBI+XyVL7pwYrbF9OjoMpGji0FOCMq4YQx0aBzxk
RuRPaQWXqnKYD8UM42SztBzAvYv6edU0LZx4
-----END CERTIFICATE-----
</ca>
EOF
            ;;
        "${NEW_CA}")
            cat <<-EOF >> "${USERHOME}/rapidseedbox.ovpn"
<ca>
-----BEGIN CERTIFICATE-----
MIIG5DCCBMygAwIBAgIURtUHqQ+Tvpuyd/qra0P7N4hAJF0wDQYJKoZIhvcNAQEL
BQAwgZsxCzAJBgNVBAYTAk5MMQwwCgYDVQQIDANSQlgxDDAKBgNVBAcMA1JCWDEZ
MBcGA1UECgwQUmFwaWRTZWVkYm94LmNvbTEZMBcGA1UECwwQcmFwaWRzZWVkYm94
LmNvbTERMA8GA1UEAwwIc2VlZGJveDExJzAlBgkqhkiG9w0BCQEWGHN1cHBvcnRA
cmFwaWRzZWVkYm94LmNvbTAeFw0yNTA0MTYxMTU3NTZaFw0zNTA0MTQxMTU3NTZa
MIGbMQswCQYDVQQGEwJOTDEMMAoGA1UECAwDUkJYMQwwCgYDVQQHDANSQlgxGTAX
BgNVBAoMEFJhcGlkU2VlZGJveC5jb20xGTAXBgNVBAsMEHJhcGlkc2VlZGJveC5j
b20xETAPBgNVBAMMCHNlZWRib3gxMScwJQYJKoZIhvcNAQkBFhhzdXBwb3J0QHJh
cGlkc2VlZGJveC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC+
OB5Uc2YsmfbZfzajvUkJU9pFaicWRsdlgdAx1koLkZmzkVuyhbRqD+zScSdLRBYY
/+2KKHlfYQh20xhRwwWk0pMJxZ5c5NXEt2F1+yk62z8yTtzZSUbUiBYWoJJbOZq3
KTtD2CntABqAKzD2WveRNU1ZUUIuHvhIGhUhz+QbRMQl27KnA5qQMFbNH+GugPVP
PVnTTh4L5xzll+69ftIlkUxfnUP1JZoXe7cuFNsn5nf0odR6YFqRQY/oK2Hzu7OH
tKbyzepw4jU2xvdxiKWOYCwjXOAQ9xJ1g+Zt+/wSzag+HdTQB4ztJNKB518Wpuhw
4fxDiTtLsaPEUq7s2kfTxPUup/mnZAZdjlhRgIfeYUIu+wSOkaC8bFSD8X7t2TTJ
LRe+WQweETYXkrsaWeE/ioc4Jgmdoz83TjOW3NcijwjPEG+7KYTb6yvaO/QOML83
VpTU1g6TYDGkk2ANrL7dfYs2F9D5qs4Zd+7qW/LpUGl3DbBuvfND9+sbEhB3Bkqt
Sm0LJKjvVzSpIqjaGY2rfwdNoPySgMxwK/OobpUiYjnMwqs6P5vKadDW6k/nCXgG
7ZRCN/UyKlyMD4VszvLMB6GHKNpLMZx1DP3oeYIkA+bkmvMbJP3vwFp5wL0vWvx3
jYBPS3HD0VYrTIi6wIgmWbRCKJ84hoANyvMMHvH2mQIDAQABo4IBHDCCARgwDAYD
VR0TBAUwAwEB/zAdBgNVHQ4EFgQUOK9QQHwoW6qNtFyY9CMmiGLCvYkwgdsGA1Ud
IwSB0zCB0IAUOK9QQHwoW6qNtFyY9CMmiGLCvYmhgaGkgZ4wgZsxCzAJBgNVBAYT
Ak5MMQwwCgYDVQQIDANSQlgxDDAKBgNVBAcMA1JCWDEZMBcGA1UECgwQUmFwaWRT
ZWVkYm94LmNvbTEZMBcGA1UECwwQcmFwaWRzZWVkYm94LmNvbTERMA8GA1UEAwwI
c2VlZGJveDExJzAlBgkqhkiG9w0BCQEWGHN1cHBvcnRAcmFwaWRzZWVkYm94LmNv
bYIURtUHqQ+Tvpuyd/qra0P7N4hAJF0wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB
CwUAA4ICAQCFcA/Z3vFmX3IfZ1ndUqHzUILT5ad6Fgu5KAMtzG/4FbIFYg9L7HJk
VSIOvRelzsRDoEutLxJsFXepTFH4+vOHZZKszH6/oRH0Kjsw6treeVs2GqMhsPma
LI29m0u9xY71ROV11RTiCnlqy5b6k6r2xLA6DOppHH1kdH5FRzmb3+G7UL2s2Aot
o51IO2p0qa5SX2RVWOGrW8lvQvGLQGroYcVnqkFdIbR67vaep5IZEUSaQGgJQah8
uiLTwWZx//rqn42VOP7tOG1XpBJSR5zfJd3L3uxqVH19E5wFUqd4U3oXcTXwD3DP
ZYLwauNsrOcaFmFca8YD86VIRUbNRBCgiiHap5kZUQNhwJncne7zEh99ZbvjNrlq
Z6DoZe1vlwUvy3VD4WLdR6VIB6VgqcaMOBlQM45XnUzHxnz6VctYzgGPjrVkpONh
PkrCRiyzCWTwejgjYThImN45zU1neSxaYZH2msuLJgpR1AO+wbZm2kBXc3S0pzKo
Bqvuw4smogbArqEHQiUohwRDusnC013HQ8XuWX111gZdbHRSi6gN0wZRF63z6OzX
GhsAaF95Y1Oa7Ee68ehw0lxNX2jkhAAUCOfswu2awfjQPVsf0w8Iz/tOUj4/+jXY
5h1Atpb/pnVFfm0RyBFD/V7Ff796gMvYgymMn/6rxiCWM4cCkGHd8w==
-----END CERTIFICATE-----
</ca>
EOF
            ;;
        *)
            ERROR "Unknown CA fingerprint: ${ca_fingerprint}"
    esac


#    JOB_END "Configuring OpenVPN"
}



function _check_mem () {
    [ ${MEMORY_K_TOTAL} -lt ${MEMORY_K_MIN} ] && return 116
    return 0
}



function _init_db () {
    local name
    local version
    local check_ver_cmd
    local pkgname
    local pkgversion
    local sys_pkg_name
    local supported_on
    local extra_vars
    local app_status
    local scope
    local is_upgradable
    local upgrade_ver_cmd
    local upgrade_ver=""
    local oAPP
    local oAPP_DIRECTORY
    local oAPP_HOME
    local oPLUGINS_DIRECTORY
    local oDELUGE_VER
    local oRT_S_VER
    local oIFS
    local tmp_file
    local __f
    local _body
    local _pkgname
    local _status
    local _password
    local _s
    local pfx
    local python_ver
    local app_idx=0
    local app_cnt


#    L_LOG_START
#    INFO "Force DB initialization"
#    L_LOG_FINISH
    __update_json_status "running" "Rebuilding APP DB"

    WGET --nolog "${SUPPORT}/installers.db" || return 3
    MV "${TMP_DIR}/installers.db" "${APP_DB}" || return 3
    WGET "${SUPPORT}/installers-whmcs.db" || return 3
    MV "${TMP_DIR}/installers-whmcs.db" "${APP_DB_WHMCS}" || return 3
    CHMOD_FILE 0600 "${APP_DB}"
    CHMOD_FILE 0600 "${APP_DB_WHMCS}"

    oAPP="${APP}"
    oAPP_VER="${APP_VER}"
    oAPP_DIRECTORY="${APP_DIRECTORY}"
    oAPP_HOME="${APP_HOME}"
    oPLUGINS_DIRECTORY="${PLUGINS_DIRECTORY}"
    oDELUGE_VER="${DELUGE_VER}"
    oRT_S_VER="${RT_S__VER}"

    #
    # Update list of upgradable standard packages
    #
    if [ "${USERUID}" = "${FIRST_UID}" ] ; then
#        JOB_START "Updating list of upgradable standard packages"
        case "${LSB_VENDOR}" in
          "CentOS"|"CentOSStream")
              ${__run_cmd} "yum list updates \
                               | awk '/Updated Packages/,0' \
                               | grep -v '^Updated Packages' \
                               | awk '{print \$1\" \"\$2}' \
                              > '${TMP_DIR}/upgradable-pkgs.list'" \
                 || RM_FILES "${TMP_DIR}/upgradable-pkgs.list"
              ;;
          "Debian"|"Devuan"|"Ubuntu")
              ${__run_cmd} "apt-get -q -y update"
              ${__run_cmd} "apt-get upgrade -s | grep -e '^Inst' | awk '{print \$2\" \"\$4}' | tr -d '(' \
                    > '${TMP_DIR}/upgradable-pkgs.list'" \
                 || RM_FILES "${TMP_DIR}/upgradable-pkgs.list"
              ;;
        esac
        if [ -n "${INSTALLAPP_TMP_DIR}" ] ; then
            if [ -d "${INSTALLAPP_TMP_DIR}" ] ; then
                if [ -e "${TMP_DIR}/upgradable-pkgs.list" ] ; then
                    CP_FILE "${TMP_DIR}/upgradable-pkgs.list" \
                            "${INSTALLAPP_TMP_DIR}/upgradable-pkgs.list"
                fi
            fi
        fi
#        JOB_END "Updating list of upgradable standard packages"
    fi

#    JOB_START "Rebuilding APP DB"

    oQUIET="${QUIET}"
    QUIET="yes"

    app_cnt="$( ${SQL_CMD} "${APP_DB}" "SELECT COUNT(*) FROM application;" )"
    while read _s ; do
        app_idx="$(( app_idx + 1 ))"
        if [ -n "${INSTALLAPP_TMP_DIR}" \
             -a -e "${INSTALLAPP_TMP_DIR}" ]
        then
            echo "$(( 100 * app_idx / app_cnt ))" > "${INSTALLAPP_TMP_DIR}/db_rebuild_progress"
        fi
        oIFS="${IFS}"
        IFS='|'
        read -ra __arr <<< "${_s}"
        IFS="${oIFS}"
        app_id="${__arr[0]}"
        name="${__arr[1]}"
        pkgname="${__arr[2]}"
        sys_pkg_name="${__arr[3]}"
        version="${__arr[4]}"
        extra_vars="${__arr[5]}"
        is_upgradable="${__arr[6]}"
        #        version=""
        #
        #        DEBUG "Application name: [${name}]"
        #
        # Check "supported" status:
        supported_on="$( ${SQL_CMD} "${APP_DB}" \
            "SELECT supported_on FROM application WHERE id=${app_id};" )"
#        RUN_LOG \
#            "echo '${supported_on}' | grep -q -E '^${LSB_VER}$|^${LSB_VER},|,${LSB_VER}$|,${LSB_VER},' 2>&1"
        if ! echo "${supported_on}" \
               | grep -q -E "^${LSB_VER}\$|^${LSB_VER},|,${LSB_VER}\$|,${LSB_VER}," >> "${LOGFILE}" 2>&1
        then
#            DEBUG "Deleting from DB: app_id == '${app_id}'"
            ${SQL_CMD} "${APP_DB}" \
                "DELETE from application WHERE id=${app_id};" >> "${LOGFILE}" 2>&1
            ${SQL_CMD} "${APP_DB_WHMCS}" \
                "DELETE from application WHERE id=${app_id};" >> "${LOGFILE}" 2>&1
            continue
        fi
        APP="${name}"
        if [ "${APP}" = "OS Update" ] ; then
            version="${LSB_DESCRIPTION}"
        fi
        SYS_PKG_NAME=""
        STORED_PASSWORD_SERVICE=""
        if [ ! -z "${extra_vars}" ] ; then
#            DEBUG "Source extra args: ${extra_vars}"
            eval "${extra_vars}"
        fi
        check_ver_cmd="$( ${SQL_CMD} "${APP_DB}" \
            "SELECT check_ver_cmd FROM application WHERE id=${app_id};" )"
        if [ ! -z "${check_ver_cmd}" ] ; then
#            DEBUG "check_ver_cmd: [${check_ver_cmd}]"
            version="$( eval "${check_ver_cmd}" )"
        fi
#        DEBUG "Version #1:       [${version}]"
        APP_VER="${version}"
#        DEBUG "Version #2:       [${version}]"
        app_status=0
        ${CHECK_APP} "${name}" "install"
        if [ $? -eq 111 ] ; then
            version="${APP_VER}"
            if [ ! -z "${DELUGE_VER}" ] ; then
#                DEBUG "DELUGE_VER: ${DELUGE_VER}"
                for pfx in "/usr/local" "/usr" ; do
                    for python_ver in "2.6" "2.7" ; do
                        if [ \
                -d "${pfx}/lib/python${python_ver}/dist-packages/deluge-${DELUGE_VER}-py${python_ver}.egg" \
                            -o \
                -d "${pfx}/lib/python${python_ver}/site-packages/deluge-${DELUGE_VER}-py${python_ver}.egg" ]
                        then
#                            DEBUG "python_ver: ${python_ver}"
                            app_status=1
                            break 2
                        fi
                    done
                done
            elif [ "${pkgname}" = "rutorrent" -o "${pkgname}" = "rtorrent" ] ; then
#                DEBUG "pkgname: ${pkgname}"
                if which rtorrent > /dev/null 2>&1 ; then
#                    DEBUG "${pkgname}: executable exists"
                    if [ -e "${USERHOME}/.rtorrent.rc" ] ; then
#                        DEBUG "${pkgname}: .rc file exists"
                        app_status=1
                    fi
                fi
            elif [ ! -z  "${RT_S_VER}" ] ; then
                if [ -d "/usr/local/rt${RT_S_VER}" ] ; then
#                    DEBUG "RT_S_VER: ${RT_S_VER}"
                    app_status=1
                fi
            elif [ "${pkgname}" = "deluge" -o "${pkgname}" = "deluge-2" ] ; then
                case "${version}" in
                  1*)
                      if [ "${pkgname}" = "deluge-2" ] ; then
                          version=""
                          upgrade_ver=""
                          app_status=0
                      else
                          app_status=1
                      fi
                      ;;
                  2*)
                      if [ "${pkgname}" = "deluge" ] ; then
                          version=""
                          upgrade_ver=""
                          app_status=0
                      else
                          app_status=1
                      fi
                      ;;
                esac
            else
                app_status=1
            fi
        fi

        upgrade_ver=""
        if [ -n "${app_status}" -a "${app_status}" != "0" ] ; then
            upgrade_ver_cmd="$( ${SQL_CMD} "${APP_DB}" \
                "SELECT upgrade_ver_cmd FROM application WHERE id=${app_id};" )"
            DEBUG "upgrade_ver_cmd: [${upgrade_ver_cmd}]"
            if [ ! -z "${upgrade_ver_cmd}" ] ; then
                if [ "${upgrade_ver_cmd}" = "STANDARD_PACKAGE" ] ; then
                    DEBUG "${pkgname}: upgrade_ver_cmd: STANDARD_PACKAGE"
                    upgrade_ver_cmd=""
                    _pkgname="${pkgname}"
                    [ ! -z "${sys_pkg_name}" ] && _pkgname="${sys_pkg_name}"
                    DEBUG "_pkgname: [${_pkgname}]"
                    if [ -e "${TMP_DIR}/upgradable-pkgs.list" ] ; then
                        DEBUG "${_pkgname}: STANDARD_PACKAGE"
                        upgrade_ver_cmd="awk -v pkg=\"\${_pkgname}\" '\$1 == pkg {print \$2}' \"\${TMP_DIR}/upgradable-pkgs.list\""
                        DEBUG "upgrade_ver_cmd: [${upgrade_ver_cmd}]"
                    fi
#                elif [ "${upgrade_ver_cmd}" = "PIP_PACKAGE" ] ; then
#                    upgrade_ver_cmd=""
#                    which pip > /dev/null 2>&1 \
#                        && upgrade_ver_cmd="pip search '${pkgname}' \
#                                              | awk -v pkg='${pkgname}' '\$1 == pkg {print \$2}' \
#                                              | tr -d '()'"
#                elif [ "${upgrade_ver_cmd}" = "PIP3_PACKAGE" ] ; then
#                    upgrade_ver_cmd=""
#                    which pip3 > /dev/null 2>&1 \
#                        && upgrade_ver_cmd="pip3 search '${pkgname}' \
#                                              | awk -v pkg='${pkgname}' '\$1 == pkg {print \$2}' \
#                                              | tr -d '()'"
                fi
                DEBUG "Running: [${upgrade_ver_cmd}]"
                [ -n "${upgrade_ver_cmd}" ] \
                    && upgrade_ver="$( eval "${upgrade_ver_cmd}" )"
            fi
            DEBUG "upgrade_ver #1:     [${upgrade_ver}]"
            [ -z "${upgrade_ver}" ] \
                && upgrade_ver="${version}"
            DEBUG "upgrade_ver #2:     [${upgrade_ver}]"
        fi
        if [ "${APP}" = "OS Update" ] ; then
            app_status=1
            if [   -e "${TMP_DIR}/upgradable-pkgs.list" \
                -a -s "${TMP_DIR}/upgradable-pkgs.list" ]
            then
                upgrade_ver="Updates available"
            else
                upgrade_ver="Up to date"
            fi
        fi
        # Service / application password
        _password=""
        if [ ${app_status} -ne 0 ] && [ -n "${STORED_PASSWORD_SERVICE}" ] && [ -e "${PASSWORDS_STORE}" ]
        then
            _password="$( awk -F: -v svc="${STORED_PASSWORD_SERVICE}" \
                                  '$1 == svc {print $2}' \
                             "${PASSWORDS_STORE}" )"
        fi
        DEBUG "_password: [${_password}]"
        version="$( echo "${version}" | tail -1 )"
        upgrade_ver="$( echo "${upgrade_ver}" | tail -1 )"
        password="$( echo "${password}" | tail -1 )"
        app_status="$( echo "${app_status}" | tail -1 )"
        s="$(
         cat <<-EOF
		UPDATE application
		SET
		   version = '${version}',
		   status = ${app_status},
		   date = $( date +'%s' ),
		   upgrade_ver = '${upgrade_ver}',
		   password = '${_password}'
		WHERE id = ${app_id};
		EOF
        )"
        for __f in \
            "${APP_DB}" \
            "${APP_DB_WHMCS}" \
        ; do
#            DEBUG "SQL INSERT: [${s}] -> [${__f}]"
#            DEBUG "[${SQL_CMD} \"${__f}\" \"${s}\"]"
            ${SQL_CMD} "${__f}" "${s}" >> "${LOGFILE}" 2>&1
        done
        APP=""
        APP_VER=""
        APP_DIRECTORY=""
        APP_HOME=""
        PLUGINS_DIRECTORY=""
        DELUGE_VER=""
        RT_S_VER=""
        STORED_PASSWORD_SERVICE=""
    done < <(${SQL_CMD} "${APP_DB}" \
                  "SELECT id,name,pkgname,sys_pkg_name,version,extra_vars,is_upgradable FROM application;")

    # Generate WHMCS JSON DB snapshot:
    echo -ne "{\"status\":\"running\",\"message\":\"Rebuilding APP DB\",\"rc\":\"0\",\"os_release\":\"${LSB_VER}\",\"application\":[" > "${APP_DB_WHMCS_JSON}.tmp"
    _body=""
    while read _s ; do
        oIFS="${IFS}"
        IFS='|'
        read -ra __arr <<< "${_s}"
        IFS="${oIFS}"
        app_id="$(        sed -e 's/"/\\"/g' <<< "${__arr[0]}" )"
        name="$(          sed -e 's/"/\\"/g' <<< "${__arr[1]}" )"
        pkgname="$(       sed -e 's/"/\\"/g' <<< "${__arr[2]}" )"
        version="$(       sed -e 's/"/\\"/g' <<< "${__arr[3]}" )"
        is_upgradable="$( sed -e 's/"/\\"/g' <<< "${__arr[4]}" )"
        upgrade_ver="$(   sed -e 's/"/\\"/g' <<< "${__arr[5]}" )"
        _status="$(       sed -e 's/"/\\"/g' <<< "${__arr[6]}" )"
        _password="$(     sed -e 's/"/\\"/g' <<< "${__arr[7]}" )"
        [ -n "${_body}" ] && _body="${_body},"
        _body="${_body}{\"id\":\"${app_id}\",\"name\":\"${name}\",\"pkgname\":\"${pkgname}\",\"version\":\"${version}\",\"is_upgradable\":\"${is_upgradable}\",\"upgrade_ver\":\"${upgrade_ver}\",\"status\":\"${_status}\",\"password\":\"${_password}\"}"
    done < <( ${SQL_CMD} "${APP_DB_WHMCS}" \
      "SELECT id,name,pkgname,version,is_upgradable,upgrade_ver,status,password FROM application;" )
    echo -ne "${_body}]}" >> "${APP_DB_WHMCS_JSON}.tmp"
    MV "${APP_DB_WHMCS_JSON}.tmp" "${APP_DB_WHMCS_JSON}"
    #
    QUIET="${oQUIET}"
    #
    ${SQL_CMD} "${APP_DB}" \
           "UPDATE version SET os_release = '${LSB_VER}';" \
        >> "${LOGFILE}" 2>&1
    ${SQL_CMD} "${APP_DB_WHMCS}" \
           "UPDATE version SET os_release = '${LSB_VER}';" \
        >> "${LOGFILE}" 2>&1
    #
#    JOB_END "Rebuilding APP DB"
    #
    APP="${oAPP}"
    APP_VER="${oAPP_VER}"
    APP_DIRECTORY="${oAPP_DIRECTORY}"
    APP_HOME="${oAPP_HOME}"
    PLUGINS_DIRECTORY="${oPLUGINS_DIRECTORY}"
    DELUGE_VER="${oDELUGE_VER}"
    RT_S_VER="${oRT_S_VER}"

    return 0
}







# ----------------------------------------------------------
# Script entry/exit functions
# ----------------------------------------------------------

function SCRIPTEXIT(){

    local _app
    local _app_id
    local _msg
    local _tm
    local _ver
    local _log_file
    local s
    local _f
    local _systemd_reload
    local _svc
    local _password
    local _monit_reload
    local _cfg _cfg_dir
    local _ovpn_restart
    local _svc_restart _svc_reconfigure
    local _ratio _stop_seed_at_ratio _stop_seed_ratio
    local _ratio_limit _ratio_limit_enabled

    timeAndDate="$( date -u )"

    # Auto-remove packages
    if    [ "${LSB_VENDOR}" = "Debian" ]     \
       || [ "${LSB_VENDOR}" = "Ubuntu" ]     \
       || [ "${LSB_VER}" = "CentOS7" ]       \
       || [ "${LSB_VER}" = "CentOSStream8" ]
    then
        ${PKG_AUTOREMOVE}
    fi
    #
    # Common tasks
    case "${LSB_VENDOR}" in
      "CentOS"|"CentOSStream")
          YUM_CLEAN
          ;;
      "Debian"|"Ubuntu")
          APT_CLEAN
          ;;
    esac

    cd ~/
#    _msg="${timeAndDate} :: finished ${operation1} '${APP}' for ${IP} - exit code: [${main_rc}]"
#    [ "${main_rc}" != "0" ] \
#        && _msg="${timeAndDate} :: Attention - @channel - finished ${operation1} '${APP}' for ${IP} - exit code: [${main_rc}]"
#    ./slackpost "rsb-installers" "${_msg}" > /dev/null 2>&1

    if [ ! -z "${main_rc}" ] ; then
        echo -e "\n\n" >> "${LOGFILE}"
        L_LOG
        JOB_NAME="${operation2} status for '${APP}'"
        LOG=""
        LOG_RC "${main_rc}"
        L_LOG_START
        [ -n "${JSON_MSG}" ] && JSON_MSG="${JSON_MSG}; "
        case "${main_rc}" in
          "0")
              JSON_MSG="${JSON_MSG}Operation succeeded"
              ;;
          "1")
              JSON_MSG="${JSON_MSG}General failure. Please look at ${LOGFILE}"
              ;;
          "111")
              JSON_MSG="${JSON_MSG}Application is already installed on VPS while 'install' operation requested"
              ;;
          "112")
              JSON_MSG="${JSON_MSG}Application is NOT installed on VPS while 'uninstall/upgrade' operation requested"
              ;;
          "113")
              JSON_MSG="${JSON_MSG}Application ${operation2} is NOT supported on current OS - ${LSB_VENDOR} ${LSB_RELEASE} ($( systemd-detect-virt ))"
              ;;
          "114")
              JSON_MSG="${JSON_MSG}Another installation procedure is in progress"
              ;;
          "115")
              JSON_MSG="${JSON_MSG}System uptime is too low ( < 10 minutes )"
              ;;
          "116")
              JSON_MSG="${JSON_MSG}Resources shortage (f.e. low VPS total memory, not enough free disk space, etc)"
              ;;
          "117")
              JSON_MSG="${JSON_MSG}Application conflicts with one (or more) already installed application(s)"
              ;;
        esac
        if [ "${main_rc}" = "0" ] ; then
            INFO  "${JSON_MSG}"
        else
            ERROR "${JSON_MSG}"
        fi
        L_LOG_FINISH
    fi

    # -- We will use exit code 1 in `flock` call...
    [ "${main_rc}" = "1" ] && main_rc="99"

    echo -e "\n\n" >> "${LOGFILE}"
    L_LOG
    echo "${FUNCNAME}: ${script_name}  RC: ${main_rc}" >> "${LOGFILE}"
    L_LOG
    echo -e "\n\n" >> "${LOGFILE}"

    if [ -e "${APP_DB}" ] ; then
        if [ "${main_rc}" = "0" ] ; then
            if [ "${operation2}" = "Installation" ] || [ "${operation2}" = "Upgrade" ] ; then
                op_status="1"
                # Restore user's password if available (except regular user accounts)
                if [ -n "${STORED_PASSWORD_SERVICE}" ] && [ -e "${PASSWORDS_STORE}" ]
                then
                    if    [ "${STORED_PASSWORD_SERVICE}" != "user" ] \
                       && [ "${STORED_PASSWORD_SERVICE}" != "userdir" ]
                    then
                        _password="$( awk -F: -v svc="${STORED_PASSWORD_SERVICE}" \
                                                 '$1 == svc {print $2}' \
                                          "${PASSWORDS_STORE}" )"
                        [ -z "${_password}" ] \
                            && _password="$( awk -F: '$1 == "user" {print $2}' \
                                          "${PASSWORDS_STORE}" )"
                        [ -z "${_password}" ] \
                            && _password="${USERPASSWORD}"
                        INFO "Changing password for '${STORED_PASSWORD_SERVICE}': [${_password}]"
                        RUN_LOG "/usr/local/bin/changeseedboxpass '${USERNAME}' '${STORED_PASSWORD_SERVICE}' '${_password}' 2>&1"
                    else
                        INFO "User account service: keep existing password"
                    fi
                fi
            elif [ "${operation2}" = "Uninstallation" ] ; then
                if [ -n "${STORED_PASSWORD_SERVICE}" ] && [ -e "${PASSWORDS_STORE}" ]
                then
                    if    [ "${STORED_PASSWORD_SERVICE}" != "user" ] \
                       && [ "${STORED_PASSWORD_SERVICE}" != "userdir" ]
                    then
                        RUN_LOG "sed -i -e '/^${STORED_PASSWORD_SERVICE}:/d' ${PASSWORDS_STORE} 2>&1"
                    fi
                fi
                op_status="0"
            else
                op_status=""
            fi
            if [ -n "${op_status}" ] ; then
                DEBUG "APP_VER #1:   [${APP_VER}]"
                oQUIET="${QUIET}"
                QUIET="yes"
                ${CHECK_APP} "${APP}" "install"
                QUIET="${oQUIET}"
                DEBUG "APP_VER #2:   [${APP_VER}]"
                _ver="${APP_VER}"
                _app="$( ${SQL_CMD} "${APP_DB}" "SELECT name FROM application WHERE name='${APP}';" )"
                _app_id="$( ${SQL_CMD} "${APP_DB}" "SELECT id FROM application WHERE name='${APP}';" )"
                _tm="$( date -u +'%s' )"
                # Re-initialize APP DB (if not disabled in the main script)
                if [ "${APP_DB_UPDATE}" = "yes" ] ; then
                    [ -e "${APP_DB}" ] && RM_FILES "${APP_DB}"
                    _init_db
                fi
            fi
        fi
    fi
    echo "${main_rc}${EXTRA_MAIN_RC}"
    >&2 echo "${main_rc}${EXTRA_MAIN_RC}"

    __update_json_status "idle" "'${APP}' ${operation2}: ${JSON_MSG}" "${main_rc}"

    # -- Emulate Ubuntu's behavior on CentOS system
    if which needs-restarting > /dev/null 2>&1 ; then
        RUN_LOG "needs-restarting -r" \
            || RUN_LOG "touch /var/run/reboot-required"
    fi

    # -- Configure PAM
    if [ -d "/etc/pam.d" ] ; then
        # -- Install/Update /usr/local/sbin/rsb-pam-acct.sh script
        [ ! -e "/usr/local/sbin/rsb-pam-acct.sh" ] \
            && touch "/usr/local/sbin/rsb-pam-acct.sh"
        if WGET --nolog "${SUPPORT}/rsb-scripts/sbin/rsb-pam-acct.sh" ; then
            if [ $? -eq 0 ] ; then
                if [ -s "${TMP_DIR}/${app}" ] ; then
                    ${__run_cmd} "diff '${TMP_DIR}/rsb-pam-acct.sh' '/usr/local/sbin/rsb-pam-acct.sh'"
                    if [ $? -ne 0 ] ; then
                        INFO "Updating 'rsb-pam-acct.sh' ..."
                        MV "${TMP_DIR}/rsb-pam-acct.sh" "/usr/local/sbin/rsb-pam-acct.sh"
                        CHOWN_FILE root:root "/usr/local/sbin/rsb-pam-acct.sh"
                        CHMOD_FILE 0700 "/usr/local/sbin/rsb-pam-acct.sh"
                    fi
                fi
            fi
        fi
        # -- Configure PAM accounting
        if [ -x "/usr/local/sbin/rsb-pam-acct.sh" ] ; then
            for _f in \
                "common-auth" \
                "sshd" \
                "thinlinc" \
            ; do
                [ -e "/etc/pam.d/${_f}" ] || continue
                if ! grep -q -E \
                        "^session\s+optional\s+pam_exec.so\s+seteuid\s+/usr/local/sbin/rsb-pam-acct.sh\s*$" \
                        "/etc/pam.d/${_f}" > /dev/null 2>&1
                then
                    echo "session optional pam_exec.so seteuid /usr/local/sbin/rsb-pam-acct.sh" \
                        >> "/etc/pam.d/${_f}"
                fi
            done
        fi
        # --
    fi

    # -- Configure OpenVPN
    _ovpn_restart="no"
    while true ; do
        if [ -e "/etc/openvpn/rsb-server.conf" ] ; then
            # -- Install/Update /etc/openvpn/rsb-server-acct.sh script
            [ ! -e "/etc/openvpn/rsb-server-acct.sh" ] \
                && touch "/etc/openvpn/rsb-server-acct.sh"
            WGET --nolog "${SUPPORT}/rsb-scripts/common/rsb-server-acct.sh" || break
            if [ -s "${TMP_DIR}/rsb-server-acct.sh" ] ; then
                if ! diff -q "${TMP_DIR}/rsb-server-acct.sh" \
                             "/etc/openvpn/rsb-server-acct.sh" > /dev/null 2>&1
                then
                    INFO "Updating '/etc/openvpn/rsb-server-acct.sh' ..."
                    MV "${TMP_DIR}/rsb-server-acct.sh" "/etc/openvpn/rsb-server-acct.sh"
                    CHOWN_FILE root:root "/etc/openvpn/rsb-server-acct.sh"
                    CHMOD_FILE 0600 "/etc/openvpn/rsb-server-acct.sh"
                fi
            fi
            cat <<-EOF > "${TMP_DIR}/rsb-server-client-connect.sh"
		#!/bin/bash

		export PATH=/usr/sbin:/usr/bin:/sbin:/bin

		#echo "RSB OpenVPN Client CONNECT ENV:" | logger
		#env | logger

		[ -e "/etc/openvpn/rsb-server-acct.sh" ] \\
		    && bash "/etc/openvpn/rsb-server-acct.sh"

		exit 0
		EOF
            if ! diff -q \
                      "${TMP_DIR}/rsb-server-client-connect.sh" \
                      "/etc/openvpn/rsb-server-client-connect.sh" > /dev/null 2>&1
            then
                MV "${TMP_DIR}/rsb-server-client-connect.sh" \
                   "/etc/openvpn/rsb-server-client-connect.sh"
                CHOWN_FILE root:root "/etc/openvpn/rsb-server-client-connect.sh"
                CHMOD_FILE 0700      "/etc/openvpn/rsb-server-client-connect.sh"
            fi
            cat <<-EOF > "${TMP_DIR}/rsb-server-client-disconnect.sh"
		#!/bin/bash

		export PATH=/usr/sbin:/usr/bin:/sbin:/bin

		#echo "RSB OpenVPN Client DISCONNECT ENV:" | logger
		#env | logger

		[ -e "/etc/openvpn/rsb-server-acct.sh" ] \\
		    && bash "/etc/openvpn/rsb-server-acct.sh"

		exit 0
		EOF
            if ! diff -q \
                      "${TMP_DIR}/rsb-server-client-disconnect.sh" \
                      "/etc/openvpn/rsb-server-client-disconnect.sh" > /dev/null 2>&1
            then
                MV "${TMP_DIR}/rsb-server-client-disconnect.sh" \
                   "/etc/openvpn/rsb-server-client-disconnect.sh"
                CHOWN_FILE root:root "/etc/openvpn/rsb-server-client-disconnect.sh"
                CHMOD_FILE 0700      "/etc/openvpn/rsb-server-client-disconnect.sh"
            fi
            if ! grep -q -E "^client-connect " \
                      "/etc/openvpn/rsb-server.conf" > /dev/null 2>&1
            then
                echo "client-connect /etc/openvpn/rsb-server-client-connect.sh" \
                    >> "/etc/openvpn/rsb-server.conf"
                _ovpn_restart="yes"
            fi
            if ! grep -q -E "^client-disconnect " \
                      "/etc/openvpn/rsb-server.conf" > /dev/null 2>&1
            then
                echo "client-disconnect /etc/openvpn/rsb-server-client-disconnect.sh" \
                    >> "/etc/openvpn/rsb-server.conf"
                _ovpn_restart="yes"
            fi
#            if ! grep -q -E "^tls-cipher " \
#                      "/etc/openvpn/rsb-server.conf" > /dev/null 2>&1
#            then
#                echo "tls-cipher \"DEFAULT:@SECLEVEL=0\"" \
#                    >> "/etc/openvpn/rsb-server.conf"
#                _ovpn_restart="yes"
#            fi
            if grep -q -E "^tls-cipher " \
                    "/etc/openvpn/rsb-server.conf" > /dev/null 2>&1
            then
                RUN_LOG "sed -i -re '/^tls-cipher /d' '/etc/openvpn/rsb-server.conf'"
                _ovpn_restart="yes"
            fi
#            if [ "${_ovpn_restart}" = "yes" ] && systemctl is-active "openvpn@rsb-server" > /dev/null 2>&1
            if [ "${_ovpn_restart}" = "yes" ] && systemctl is-enabled "openvpn@rsb-server" > /dev/null 2>&1
            then
                RUN_LOG "systemctl restart openvpn@rsb-server 2>&1"
            fi
        fi
        break
    done

    # -- Configure Wireguard
    if [ -e "/etc/wireguard/wg0.conf" ] ; then
        while true ; do
            # -- Install/Update /etc/wireguard/rsb-wg-acct.sh script
            [ ! -e "/etc/wireguard/wg_regenhtml.sh" ] \
                && touch "/etc/wireguard/wg_regenhtml.sh"
            [ ! -e "/etc/wireguard/rsb-wg-acct.sh" ] \
                && touch "/etc/wireguard/rsb-wg-acct.sh"
            WGET --nolog "${SUPPORT}/rsb-scripts/common/wg_regenhtml.sh" || break
            if [ -s "${TMP_DIR}/wg_regenhtml.sh" ] ; then
                if ! diff -q "${TMP_DIR}/wg_regenhtml.sh" \
                             "/etc/wireguard/wg_regenhtml.sh" > /dev/null 2>&1
                then
                    INFO "Updating '/etc/wireguard/wg_regenhtml.sh' ..."
                    MV "${TMP_DIR}/wg_regenhtml.sh" "/etc/wireguard/wg_regenhtml.sh"
                    CHOWN_FILE root:root "/etc/wireguard/wg_regenhtml.sh"
                    CHMOD_FILE 0600 "/etc/wireguard/wg_regenhtml.sh"
                fi
            fi
            WGET --nolog "${SUPPORT}/rsb-scripts/common/rsb-wg-acct.sh" || break
            if [ -s "${TMP_DIR}/rsb-wg-acct.sh" ] ; then
                if ! diff -q "${TMP_DIR}/rsb-wg-acct.sh" \
                             "/etc/wireguard/rsb-wg-acct.sh" > /dev/null 2>&1
                then
                    INFO "Updating '/etc/wireguard/rsb-wg-acct.sh' ..."
                    MV "${TMP_DIR}/rsb-wg-acct.sh" "/etc/wireguard/rsb-wg-acct.sh"
                    CHOWN_FILE root:root "/etc/wireguard/rsb-wg-acct.sh"
                    CHMOD_FILE 0600 "/etc/wireguard/rsb-wg-acct.sh"
                fi
            fi
            break
        done
    fi


    # ------------------------------------
    # -- Configure torrent clients ratio
    # ------------------------------------

    RUN_LOG "/usr/local/bin/set-ratio on"

    # ------------------------------------

    # -- qBittorrent configuration settings
    _cfg="${USERHOME}/.config/qBittorrent/qBittorrent.conf"
    _cfg_dir="$( dirname "${_cfg}" )"
    # --
    if    [ -e "${_cfg}" ] \
       && command -v "ldd" > /dev/null 2>&1 \
       && command -v "crudini" > /dev/null 2>&1
    then
        # -- libtorrent 2.*
        if ldd $( which qbittorrent-nox ) 2>&1 \
              | grep -q -E 'not a dynamic|^\s*libtorrent-rasterbar\.so\.2\.' 2>/dev/null
        then
            _svc_restart="no"
            # -- Memory leak issue
            # -- https://github.com/qbittorrent/qBittorrent/issues/18302
            s="$( crudini --get "${_cfg}" "BitTorrent" "Session\DiskIOType" 2> /dev/null )"
            if [ "${s}" != "Posix" ] ; then
                JOB_START "qBittorrent: Configure Session\DiskIOType = Posix"
                if systemctl is-active "qbittorrent@${USERNAME}" > /dev/null 2>&1 ; then
                    SVC_STOP "qbittorrent@${USERNAME}"
                    _svc_restart="yes"
                fi
                RUN_LOG "crudini --set '${_cfg}' 'BitTorrent' 'Session\DiskIOType' 'Posix' 2>&1"
                RUN_LOG "sed -i -re 's/\s+=\s+/=/' '${_cfg}'"
                JOB_END "qBittorrent: Configure Session\DiskIOType = Posix"
            fi
            if [ "${_svc_restart}" = "yes" ] ; then
                SVC_RESTART "qbittorrent@${USERNAME}"
            fi
        fi
    fi

    # -- qBittorrent (Latest) configuration settings
    _cfg="${USERHOME}/.config/qBittorrent_static/config/qBittorrent.conf"
    _cfg_dir="$( dirname "${_cfg}" )"
    # --
    if    [ -e "${_cfg}" ] \
       && command -v "crudini" > /dev/null 2>&1
    then
        _svc_restart="no"
        # -- Memory leak issue
        # -- https://github.com/qbittorrent/qBittorrent/issues/18302
        s="$( crudini --get "${_cfg}" "BitTorrent" "Session\DiskIOType" 2> /dev/null )"
        if [ "${s}" != "Posix" ] ; then
            JOB_START "qBittorrent (Latest): Configure Session\DiskIOType = Posix"
            if systemctl is-active "qbittorrent-static@${USERNAME}" > /dev/null 2>&1 ; then
                SVC_STOP "qbittorrent-static@${USERNAME}"
                _svc_restart="yes"
            fi
            RUN_LOG "crudini --set '${_cfg}' 'BitTorrent' 'Session\DiskIOType' 'Posix' 2>&1"
            RUN_LOG "sed -i -re 's/\s+=\s+/=/' '${_cfg}'"
            JOB_END "qBittorrent (Latest): Configure Session\DiskIOType = Posix"
        fi
        if [ "${_svc_restart}" = "yes" ] ; then
            SVC_RESTART "qbittorrent-static@${USERNAME}"
        fi
    fi

    #
    # -- ruTorrent 4 UI configuration settings
    #
    # -- Configuration changes
    _cfg="/var/www/rutorrent4/conf/users/${USERNAME}/config.php"
    if [ -e "${_cfg}" ] ; then
#        # !!! DEBUG !!!
#        sed -i -r -e '/\$do_diagnostic\s*=\s*.*$/d' "${_cfg}" > /dev/null 2>&1
#        sed -i -r -e '/\$al_diagnostic\s*=\s*.*$/d' "${_cfg}" > /dev/null 2>&1
#        # !!! ----- !!!
        if ! grep -q -E '^\s*\$do_diagnostic\s*=\s*false;' "${_cfg}" ; then
            sed -i -r -e '/\$do_diagnostic\s*=\s*.*$/d' "${_cfg}" > /dev/null 2>&1
            echo '  $do_diagnostic = false;' >> "${_cfg}"
        fi
        if ! grep -q -E '^\s*\$al_diagnostic\s*=\s*false;' "${_cfg}" ; then
            sed -i -r -e '/\$al_diagnostic\s*=\s*.*$/d' "${_cfg}" > /dev/null 2>&1
            echo '  $al_diagnostic = false;' >> "${_cfg}"
        fi
    fi
    # ---- Install 'filemanager' plugin if needed
    if    [ -d "/var/www/rutorrent4/plugins" ] \
       && [ ! -d "/var/www/rutorrent4/plugins/filemanager" ]
    then
        if GIT_CLONE "https://github.com/nelu/rutorrent-filemanager" \
                     "${TMP_DIR}/rutorrent-filemanager"
        then
            CP_DIR "${TMP_DIR}/rutorrent-filemanager" \
                   "/var/www/rutorrent4/plugins/filemanager"
        fi
    fi

    # -- Relocate `plexmediaserver` if necessary
    case "${script_name}" in
        "__rebuild_db.sh" \
      | "install_rsb_dashboard.sh" \
      | "uninstall_rsb_dashboard.sh" \
      | "upgrade_rsb_dashboard.sh" \
      )
          # --- Skip this for now
          ;;
      *)
          _cfg_dir="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server"
        if id "plex" > /dev/null 2>&1 \
             && [ "${main_rc}" = "0" ] \
             && mountpoint /data > /dev/null 2>&1 \
             && [ -e "${_cfg_dir}" ] \
             && [ ! -L "${_cfg_dir}" ] \
             && command -v relocate-app-data > /dev/null 2>&1
        then
            RUN_LOG "relocate-app-data -a plex 2>&1"
        fi
        ;;
    esac


    # -- Restart fragile services if necessary
    for _svc in \
        "seedbox-cp-qmgr.service" \
    ; do
        if systemctl is-enabled "${_svc}" > /dev/null 2>&1 ; then
            if ! systemctl status "${_svc}" > /dev/null 2>&1 ; then
                RUN_LOG "systemctl restart '${_svc}' 2>&1"
            fi
        fi
    done

    # -- Update HTTPS configs
    if [ "${USE_APACHE2}" = "yes" ] ; then
        if which "easy-ssl" > /dev/null 2>&1 ; then
            INFO "*********************************************"
            INFO "***   Updating 'easy-ssl' configuration   ***"
            INFO "*********************************************"
            if [ -e "/etc/ssl/a.seedbox.vip/cert.pem" ] ; then
                _flag=""
                if grep -q -E '//installers\.seedbox\.vip/' <<< "${SUPPORT}" ; then
                    _flag="-t"
                fi
#                RUN_LOG "easy-ssl -b ${_flag} 2>&1" || break
                RUN_LOG "easy-ssl -b ${_flag} 2>&1"
            fi
        fi
    fi

    #
    # -- RSB Dashboard installation
    #
    while true ; do
        if [ "${LSB_VENDOR}" != "Ubuntu" ] ; then
            break
        fi
        if [ ${main_rc} -ne 0 ] ; then
            INFO "'RSB Dashboard' installation/upgrade check - skip for now (main_rc = ${main_rc})"
            break
        fi
        if ! ${PKG_CHECK} "${APACHE_SVC}" ; then
            INFO "'RSB Dashboard' installation/upgrade check - '${APACHE_SVC}' is not installed"
            break
        fi
        case "${script_name}" in
            "__rebuild_db.sh" \
          | "install_rsb_dashboard.sh" \
          | "uninstall_rsb_dashboard.sh" \
          | "upgrade_rsb_dashboard.sh" \
          )
              INFO "'RSB Dashboard' installation/upgrade check - skip for now (${script_name} stage)"
              break
              ;;
        esac
#        DIST_UPGRADE_ENABLED="no"
        __rsb_scripts_url="$( sed -e 's|^\(.*\)/support|\1|' <<< "${SUPPORT}" )"
        INFO "RSB Dashboard: Checking if installation/upgrade is required"
        if [ ! -d "/var/www/dashboard" ] ; then
            # ---- New installation
            if ! WGET "${__rsb_scripts_url}/install_rsb_dashboard.sh" ; then
                ERROR "RSB Dashboard: Unable to fetch installation script: ${__rsb_scripts_url}/install_rsb_dashboard.sh"
                break
            fi
            INFO "RSB Dashboard: Installing"
            oWD=$( pwd )
            cd "${TMP_DIR}"
            RUN_LOG "bash ./install_rsb_dashboard.sh > /dev/null 2>&1"
            cd "${oWD}"
        else
            # Upgrade?
            _curr_ver="$( grep -E '\$version\s+=\s+' \
                               "/var/www/dashboard/inc/config.php" \
                            | awk '{print $NF}' | tr -d '";v' )"
            [ -z "${_curr_ver}" ] && break
            _upgrade_ver="$( wget -q -O - "${SUPPORT}/rsb-dashboard/version.id" )"
            [ -z "${_upgrade_ver}" ] && break
            if [ "${_curr_ver}" != "${_upgrade_ver}" ] ; then
                WGET "${__rsb_scripts_url}/upgrade_rsb_dashboard.sh" || break
                if ! WGET "${__rsb_scripts_url}/upgrade_rsb_dashboard.sh" ; then
                    ERROR "RSB Dashboard: Unable to fetch upgrade script: ${__rsb_scripts_url}/upgrade_rsb_dashboard.sh"
                    break
                fi
                INFO "RSB Dashboard: Upgrading: ${_curr_ver} --> ${_upgrade_ver}"
                oWD=$( pwd )
                cd "${TMP_DIR}"
                RUN_LOG "bash ./upgrade_rsb_dashboard.sh > /dev/null 2>&1"
                cd "${oWD}"
            else
                INFO "RSB Dashboard: Up to date: Version ${_curr_ver}"
            fi
        fi
        break
    done
    # --

    # -- Configure `monit`
    _monit_reload="no"
    if [ -e "/etc/monit/conf.d/apache2" ] ; then
        if grep -q -E '^\s*if\s*.*then\s*timeout\s*$' "/etc/monit/conf.d/apache2"
        then
            _monit_reload="yes"
            RUN_LOG "sed -i -r -e 's/(^\s*if\s*.*then\s*timeout\s*)$/#\1/' '/etc/monit/conf.d/apache2' 2>&1"
        fi
    fi
    [ "${_monit_reload}" = "yes" ] \
        && MONIT_RELOAD


    # -- Configure/fix `ttyd`
    cat <<-EOF > "${TMP_DIR}/ttyd.service"
	[Unit]
	Description=Terminal over HTTP
	#Required=sshd.service
	After=sshd.service

	[Service]
	User=user
	Group=user
	Type=simple
	WorkingDirectory=/home/user
	ExecStart=/usr/local/bin/ttyd -W -a -i 127.0.0.1 -p 4200 CLI
	ExecReload=/bin/kill -HUP \$MAINPID
	KillMode=process
	Restart=on-abort

	[Install]
	WantedBy=multi-user.target
	EOF

    if [ ! -e "/etc/systemd/system/ttyd.service" ] ; then
        _restart_ttyd="yes"
        INFO "Installing 'ttyd' service"
        CP_FILE "${TMP_DIR}/ttyd.service" \
                "/etc/systemd/system/ttyd.service"
    else
        if ! diff "${TMP_DIR}/ttyd.service" \
                  "/etc/systemd/system/ttyd.service" \
                 > /dev/null 2>&1
        then
            _restart_ttyd="yes"
            INFO "Updating 'ttyd' service file"
            CP_FILE "${TMP_DIR}/ttyd.service" \
                    "/etc/systemd/system/ttyd.service"
        fi
    fi

    # -- Clear terminal when ttyd `screen` session detached
    # -- https://unix.stackexchange.com/questions/21707/clear1-after-detach-in-gnu-screen
#    echo "termcapinfo xterm*|rxvt*  te=\E[H\E[2J:ti=\E[H\E[2J:" \
#        > "/var/www/dashboard/.screenrc-rsb-dashboard"
    if [[ -e "/var/www/dashboard" ]]; then
        echo "termcapinfo xterm*|rxvt*  te=\E[?1049l:ti=\E[?1049h:" \
            > "/var/www/dashboard/.screenrc-rsb-dashboard"
    fi

    if [ "${_restart_ttyd}" = "yes" ] ; then
        SYSTEMD_RELOAD
        SVC_ENABLE  "ttyd"
        SVC_RESTART "ttyd"
    fi

    # -- Cleanup temporary storage:
    [ -e "${tmp}" ] && RM_FILES "${tmp}"
    ${__run_cmd} "rm -rf ${TMP_DIR}"
    ${__run_cmd} "rm -vf ./${LIBRARY}"
    ${__run_cmd} "rm -vf /tmp/${LIBRARY}"

    echo -e "\n\n\n\n\n\n" >> "${LOGFILE}"

    # Exit the script finally
    exit ${main_rc}

}


function VALIDATE_USER() {
#    JOB_START "Validating user '${USERNAME}'"
    USERUID="$( id -u "${USERNAME}" 2>/dev/null )"
    INFO "USERUID: [${USERUID}]"
    if [ -z "${USERUID}" ] ; then
        ERROR "'${USERNAME}': account not found !"
        INSTALLAPP_MSG "'${USERNAME}': account not found !"
        ERROR "Can not continue. Exiting..."
        L_LOG
        main_rc=1
        SCRIPTEXIT
    fi
#    JOB_END "Validating user '${USERNAME}'"
}


function SCRIPTENTRY(){

    local app
    local supp
    local __pkg
    local __f
    local __d

    timeAndDate="$( date -u )"

    MEMORY_K_TOTAL="$( free -k | awk '$1 == "Mem:" {print $2}' )"
    DISK_K_FREE="$( df -k / | awk '$NF == "/" {print $4}' )"
    DISK_USED_PCT="$( df -h --output=pcent / | tail -1 | tr -d '[[:space:]]%' )"

    __run_cmd="__run_nolog"
    [ "${DEBUG}" = "yes" ] && __run_cmd="__run_log"

    [ -e "${LOGFILE}" -a ! -s "${LOGFILE}" ] \
        && echo -e "\n\n\n\n" >> "${LOGFILE}"
    L_LOG_START
    L_LOG
    echo "${FUNCNAME}: Script:             ${script_name} (v. ${SCRIPT_VER})" >> "${LOGFILE}"
    echo "${FUNCNAME}: Library:            ${LIBRARY} (v. ${LIB_VER})" >> "${LOGFILE}"
    echo "${FUNCNAME}: RSB_URL:            ${RSB_URL}" >> "${LOGFILE}"
    [ ! -z "${OCI_BOOTSTRAP_SCRIPT_VER}" ] && \
    echo "${FUNCNAME}: OCI bootstrap:      (v. ${OCI_BOOTSTRAP_SCRIPT_VER})" >> "${LOGFILE}"
    echo "${FUNCNAME}: Memory (Kb):        ${MEMORY_K_TOTAL}" >> "${LOGFILE}"
    echo "${FUNCNAME}: Disk Free (Kb):     ${DISK_K_FREE}" >> "${LOGFILE}"
    echo "${FUNCNAME}: Disk Used:          ${DISK_USED_PCT} %" >> "${LOGFILE}"
    echo "${FUNCNAME}: USERNAME:           ${USERNAME}" >> "${LOGFILE}"
    echo "${FUNCNAME}: USERPASSWORD:       ${USERPASSWORD}" >> "${LOGFILE}"
    echo "${FUNCNAME}: USERHOME:           ${USERHOME}" >> "${LOGFILE}"
    echo "${FUNCNAME}: DIST_UPGRADE:       ${DIST_UPGRADE_ENABLED}" >> "${LOGFILE}"
    echo "${FUNCNAME}: TMP_DIR:            ${TMP_DIR}" >> "${LOGFILE}"
    [ ! -z "${INSTALLAPP_TMP_DIR}" ] && \
    echo "${FUNCNAME}: INSTALLAPP_TMP_DIR: ${INSTALLAPP_TMP_DIR}" >> "${LOGFILE}"
    L_LOG
    L_LOG_FINISH

#    ./slackpost "rsb-installers" "${timeAndDate} :: started ${operation1} '${APP}' for ${IP}" > /dev/null 2>&1

    #
    # Perform common installation tasks
    #

    # Update reboot counter
    [ ! -d "/var/local/stats" ] \
        && RUN_LOG "mkdir -vp /var/local/stats"

    [ ! -e "/var/local/stats/reboot.count" ] \
        && echo "1" > "/var/local/stats/reboot.count"
    if [ ! -e "/etc/rsb.init.d/000.reboot-count.sh" ] ; then
        cat <<-EOF > /etc/rsb.init.d/000.reboot-count.sh
		#!/bin/bash

		# Update reboot counter

		[ ! -d "/var/local/stats" ] \\
		    && mkdir -p /var/local/stats > /dev/null 2>&1

		if [ -e "/var/local/stats/reboot.count" ] ; then
		    _cnt="\$( cat "/var/local/stats/reboot.count" )"
		    _cnt="\$(( _cnt + 1 ))"
		else
		    _cnt="1"
		fi

		echo "\${_cnt}" > "/var/local/stats/reboot.count"

		exit 0
		EOF
    fi


    __update_json_status "running" "Preparing environment"

    if ! MKDIR_TMP ; then
        main_rc=1
        SCRIPTEXIT
    fi

    # Fail if there is not enough free disk space
    if [ ${DISK_USED_PCT} -ge $(( 100 - MIN_HDD_SPACE_PCT )) ] ; then
        main_rc=116
        SCRIPTEXIT
    fi

    if [ "${INSTALL_MODE}" != "interactive" ] ; then
        # Check for system uptime and exit with RC=115 if uptime is less than 10 minutes
        _sys_uptime="$( awk -F. '{print $1}' "/proc/uptime" )"
        if [ ${_sys_uptime} -lt 600 ] ; then
            main_rc=115
            SCRIPTEXIT
        fi
    fi


    # -- Remove some garbage
    for __f in \
        "/etc/rsb.init.d/apps-init.sh" \
    ; do
        [ -e "${__f}" ] && RM_FILES "${__f}"
    done

    ${__run_cmd} "which lsb_release 2>&1"
    if [ $? -ne 0 ] ; then
        if [ -f "/etc/redhat-release" ] ; then
            LSB_VENDOR="$( sed -e 's|\(.*\)\s*release\s*.*$|\1|' "/etc/redhat-release" )"
            LSB_RELEASE="$( awk '{print $3}' "/etc/redhat-release" )"
            LSB_CODENAME="$( awk '{print $NF}' "/etc/redhat-release" | tr -d '()' )"
            LSB_DESCRIPTION="$( cat "/etc/redhat-release" )"
        elif [ -f "/etc/lsb-release" ] ; then
            . "/etc/lsb-release"
            LSB_VENDOR="${DISTRIB_ID}"
            LSB_RELEASE="${DISTRIB_RELEASE}"
            LSB_CODENAME="${DISTRIB_CODENAME}"
            LSB_DESCRIPTION="${DISTRIB_DESCRIPTION}"
        else
            ERROR "Can not determine Linux distribution, sorry!"
            INSTALLAPP_MSG "Can not determine Linux distribution, sorry!"
            main_rc=1
            SCRIPTEXIT
        fi
    else
        LSB_VENDOR="$( lsb_release -si 2>&1 )"
        LSB_RELEASE="$( lsb_release -sr )"
        LSB_CODENAME="$( lsb_release -sc )"
        LSB_DESCRIPTION="$( lsb_release -sd \
                             | sed -e 's/^\"\(.*\)\"$/\1/' \
                             | sed -e 's/^[[:space:]]*//' \
                             | sed -e 's/[[:space:]]*$//' \
                          )"
    fi
    # LSB info "normalization"
    case "${LSB_VENDOR}" in
      "Devuan")
          if [ "${LSB_RELEASE}" = "jessie" ] ; then
              LSB_RELEASE="1.0"
              LSB_CODENAME="jessie"
          fi
          ;;
    esac
    #
    LSB_VENDOR="$( echo "${LSB_VENDOR}" | awk '{print $1}' )"
    LSB_VENDOR_L="$( echo "${LSB_VENDOR}" | tr '[:upper:]' '[:lower:]' )"
    case "${LSB_VENDOR}" in
      "CentOS"|"CentOSStream"|"Debian")
          LSB_RELEASE="$( echo "${LSB_RELEASE}" | awk -F'.' '{print $1}' )"
          ;;
    esac
    LSB_VER="${LSB_VENDOR}${LSB_RELEASE}"

    if [ "${DEBUG}" = "yes" ] ; then
        DEBUG "LSB_VENDOR:       [${LSB_VENDOR}]"
        DEBUG "LSB_RELEASE:      [${LSB_RELEASE}]"
        DEBUG "LSB_CODENAME:     [${LSB_CODENAME}]"
        DEBUG "LSB_DESCRIPTION:  [${LSB_DESCRIPTION}]"
        DEBUG "LSB_VENDOR_L:     [${LSB_VENDOR_L}]"
        DEBUG "LSB_VER:          [${LSB_VER}]"
    fi

    # Check "supported" status:
    supp="no"
    __run_nolog "echo '${SUPPORTED_ON}' | grep -q -e '^${LSB_VER}$' 2>&1" && supp="yes"
    __run_nolog "echo '${SUPPORTED_ON}' | grep -q -e '^${LSB_VER},' 2>&1" && supp="yes"
    __run_nolog "echo '${SUPPORTED_ON}' | grep -q -e ',${LSB_VER}$' 2>&1" && supp="yes"
    __run_nolog "echo '${SUPPORTED_ON}' | grep -q ',${LSB_VER},' 2>&1"    && supp="yes"
    if [ "${supp}" = "no" ] ; then
        ERROR "${APP}: not supported on ${LSB_VER}"
        INSTALLAPP_MSG "${APP}: not supported on ${LSB_VER}"
        main_rc=113
        SCRIPTEXIT
    fi

    #
    # Remove/backup obsolete/bad repositories
    # 1. Debian/Ubuntu:
    for __f in \
        "dawidd0811-ubuntu-neofetch-focal.list" \
        "izzysoft.list" \
        "getdeb.list" \
        "palemoon.list" \
        "brave-${LSB_CODENAME}.list" \
    ; do
        [ -f "/etc/apt/sources.list.d/${__f}" ] \
            && MV "/etc/apt/sources.list.d/${__f}" "/etc/apt/sources.list.d/${__f}.BAK"
    done
    #
    case "${LSB_VER}" in
      "Ubuntu16.04")
          for __f in \
              "dropbox.list" \
              "emby-server.list" \
              "jd-team-ubuntu-jdownloader-focal.list" \
              "openvpn24.list" \
              "pydio.list" \
              "syncthing.list" \
              "waterfox.list" \
              "webupd8team-ubuntu-java-focal.list" \
          ; do
              [ -f "/etc/apt/sources.list.d/${__f}" ] \
                  && MV "/etc/apt/sources.list.d/${__f}" "/etc/apt/sources.list.d/${__f}.BAK"
          done
          ;;
    esac

    #
    # Set on hold some packages
    #
    # 1. "Weird" build in PPA for Ubuntu
    if ls -A /etc/apt/sources.list.d/x2go-ubuntu* > /dev/null 2>&1 ; then
        if [ ! -e "/etc/apt/sources.list.d/.x2go.unhold" ] ; then
            RUN_LOG "apt-mark hold x2go* 2>&1"
        fi
    fi


    SQL_CMD="sqlite3"

    case "${LSB_VENDOR}" in
      "CentOS"|"CentOSStream")
          CHECK_APP="CHECK_APP_YUM"
          PKG_VER="RPM_VER"
          PKG_INSTALL="YUM_INSTALL"
          PKG_RAW_INSTALL="RPM_INSTALL"
          PKG_REMOVE="YUM_REMOVE"
          PKG_AUTOREMOVE="YUM_AUTOREMOVE"
          PKG_UPDATE="YUM_DIST_UPGRADE"
          DIST_UPGRADE="YUM_DIST_UPGRADE"
          PKG_CHECK="RPM_CHECK"
          SSHD_SVC="sshd"
          APACHE_SVC="httpd"
          APACHE_CONF_DIR="/etc/httpd"
          APACHE_USER="apache"
          APACHE_GROUP="apache"
          A2ENSITE="a2ensite_centos"
          A2DISSITE="a2dissite_centos"
          REQ_PACKAGES="acl bash-completion bind-utils sqlite mc cronie logrotate less curl jq yum-utils inotify-tools crudini iperf3 libpwquality GeoIP"
          if [ "${LSB_RELEASE}" != "8" ] ; then
              REQ_PACKAGES="${REQ_PACKAGES} monit"
          fi

          if [ "${LSB_RELEASE}" = "6" ] ; then
              SVC_SYSTEM="init"
              ${PKG_CHECK} "nux-dextop-release" || ${PKG_INSTALL} --url \
                    "http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm"
          elif [ "${LSB_RELEASE}" = "7" ] ; then
              SVC_SYSTEM="systemd"
              ${PKG_CHECK} "nux-dextop-release" \
                  || ${PKG_INSTALL} --url \
                     "http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm"
          elif [ "${LSB_RELEASE}" = "8" ] ; then
              SVC_SYSTEM="systemd"
          fi
          ;;

      "Debian"|"Devuan"|"Ubuntu")
          CHECK_APP="CHECK_APP_DEB"
          PKG_VER="DPKG_VER"
          PKG_INSTALL="APT_INSTALL"
          PKG_RAW_INSTALL="DPKG_INSTALL"
          PKG_REMOVE="APT_REMOVE"
          PKG_AUTOREMOVE="APT_AUTOREMOVE"
          PKG_UPDATE="APT_UPDATE"
          DIST_UPGRADE="APT_DIST_UPGRADE"
          PKG_CHECK="DPKG_CHECK"
          SVC_SYSTEM="systemd"
          [ "${LSB_VENDOR}" = "Devuan" ] && SVC_SYSTEM="init"
          SSHD_SVC="ssh"
          APACHE_SVC="apache2"
          APACHE_CONF_DIR="/etc/apache2"
          APACHE_USER="www-data"
          APACHE_GROUP="www-data"
          A2ENSITE="a2ensite"
          A2DISSITE="a2dissite"
          REQ_PACKAGES="acl bash-completion sqlite3 mc cron logrotate less dnsutils diffutils software-properties-common curl jq inotify-tools virt-what crudini whois sysstat uuid-runtime iperf3 libpam-pwquality geoip-bin"
          case "${LSB_CODENAME}" in
            "buster")
                # `monit` is not available in Debian 10 (buster)
                ;;
            *)
                REQ_PACKAGES="${REQ_PACKAGES} monit"
                ;;
          esac

          case "${LSB_VENDOR}" in
            "Debian"|"Devuan")
                REQ_PACKAGES="${REQ_PACKAGES} apt-transport-https dirmngr"
                if [ "${LSB_VENDOR}" = "Devuan" ] ; then
                    REQ_PACKAGES="${REQ_PACKAGES} chkconfig"
                fi
                # `fuse*` is not good in our containers :(
                APT_REMOVE \
                    "fuse" \
                    "keyboard-configuration" \
                    "libfuse2" \
                    "libntfs-3g883" \
                    "sshfs"
                cat <<-EOF > "/etc/apt/preferences.d/099fuse.pref"
			Package: fuse
			Pin: release *
			Pin-Priority: -1

			Package: fuseiso
			Pin: release *
			Pin-Priority: -1

			Package: libfuse2
			Pin: release *
			Pin-Priority: -1

			Package: keyboard-configuration
			Pin: release *
			Pin-Priority: -1

			Package: libntfs-3g883
			Pin: release *
			Pin-Priority: -1

			Package: sshfs
			Pin: release *
			Pin-Priority: -1

			Package: acpi-support
			Pin: release *
			Pin-Priority: -1

			Package: acpid
			Pin: release *
			Pin-Priority: -1

			Package: reload
			Pin: release *
			Pin-Priority: -1

			Package: fwupd
			Pin: release *
			Pin-Priority: -1
			EOF

                cat <<-EOF > "/etc/apt/preferences.d/099firewalld.pref"
			Package: firewalld
			Pin: release *
			Pin-Priority: -1

			Package: nftables
			Pin: release *
			Pin-Priority: -1
			EOF
                ;;
            "Ubuntu")
                REQ_PACKAGES="${REQ_PACKAGES} ppa-purge"
                case "${LSB_RELEASE}" in
                  "16.04")
                       REQ_PACKAGES="${REQ_PACKAGES} apt-transport-https debconf-utils python-software-properties"
                       ;;
                esac
                #
                cat <<-EOF > "/etc/apt/preferences.d/099acpi.pref"
			Package: acpi-support
			Pin: release *
			Pin-Priority: -1

			Package: acpid
			Pin: release *
			Pin-Priority: -1

			Package: reload
			Pin: release *
			Pin-Priority: -1

			Package: fwupd
			Pin: release *
			Pin-Priority: -1
			EOF

                cat <<-EOF > "/etc/apt/preferences.d/099firewalld.pref"
			Package: firewalld
			Pin: release *
			Pin-Priority: -1

			Package: nftables
			Pin: release *
			Pin-Priority: -1
			EOF
                ;;
          esac

          case "${LSB_VENDOR}" in
            "Debian")
                case "${LSB_VER}" in
                  "Debian9"|"Debian10")
                      DEB_MIRROR="http://ftp.nl.debian.org/debian"
                      cat <<-EOF > "/etc/apt/sources.list"
			deb ${DEB_MIRROR} ${LSB_CODENAME} main contrib non-free
			deb-src ${DEB_MIRROR} ${LSB_CODENAME} main contrib non-free
			deb ${DEB_MIRROR} ${LSB_CODENAME}-updates main contrib non-free
			deb-src ${DEB_MIRROR} ${LSB_CODENAME}-updates main contrib non-free
			deb http://security.debian.org/ ${LSB_CODENAME}/updates main contrib non-free
			deb-src http://security.debian.org/ ${LSB_CODENAME}/updates main contrib non-free
			EOF
                      ;;
                esac
                # Not all Debian releases has `backports` repository
                case "${LSB_VER}" in
                  "Debian9"|"Debian10")
                      cat <<-EOF >> "/etc/apt/sources.list"
			deb ${DEB_MIRROR} ${LSB_CODENAME}-backports main contrib non-free
			deb-src ${DEB_MIRROR} ${LSB_CODENAME}-backports main contrib non-free
			EOF
                      ;;
                esac
                ;;
#            "Devuan")
#                cat <<-EOF > "/etc/apt/sources.list"
#		deb http://auto.mirror.devuan.org/merged ${LSB_CODENAME} main
#		deb http://auto.mirror.devuan.org/merged ${LSB_CODENAME}-updates main
#		deb http://auto.mirror.devuan.org/merged ${LSB_CODENAME}-security main
#		EOF
#                ;;
            "Ubuntu")
                cat <<-EOF > "/etc/apt/sources.list"
		deb http://archive.ubuntu.com/ubuntu ${LSB_CODENAME} main restricted universe multiverse
		deb http://archive.ubuntu.com/ubuntu ${LSB_CODENAME}-updates main restricted universe multiverse
		deb http://security.ubuntu.com/ubuntu ${LSB_CODENAME}-backports main restricted universe multiverse
		deb http://security.ubuntu.com/ubuntu ${LSB_CODENAME}-security main restricted universe multiverse
		EOF
                ;;
          esac
          ;;
      *)
          ERROR          "Unsupported distribution: '${LSB_VENDOR}'"
          INSTALLAPP_MSG "Unsupported distribution: '${LSB_VENDOR}'"
          L_LOG
          main_rc=1
          SCRIPTEXIT
          ;;
    esac

    #
    # Remove outdated / deprecated repositories
    #
    if [ -e "/etc/apt/sources.list.d/certbot-ubuntu-certbot-${LSB_CODENAME}.list" ]
    then
        # -- https://launchpad.net/~certbot/+archive/ubuntu/certbot
        DEL_APT_REPO "ppa:certbot/certbot"
        RUN_LOG "rm -vf /etc/apt/sources.list.d/certbot-ubuntu-certbot-${LSB_CODENAME}.list*"
        APT_UPDATE
    fi

    # -- Fix Plex repository
    # Remove legacy repository if exists
    if [[ -f "/etc/apt/sources.list.d/plexmediaserver.list" ]]; then
        RM_FILES "/etc/apt/sources.list.d/plexmediaserver.list"

        ADD_APT_REPO "https://repo.plex.tv/deb" "public" "main" "https://downloads.plex.tv/plex-keys/PlexSign.v2.key" "plex" || break

        APT_UPDATE
    fi

    #
    # Update list of upgradable standard packages
    #
#    JOB_START "Updating list of upgradable standard packages"
    case "${LSB_VENDOR}" in
      "CentOS"|"CentOSStream")
          ${__run_cmd} "yum list updates \
                          | awk '/Updated Packages/,0' \
                          | grep -v '^Updated Packages' | awk '{print \$1\" \"\$2}' \
                > '${TMP_DIR}/upgradable-pkgs.list'" \
             || RM_FILES "${TMP_DIR}/upgradable-pkgs.list"
          ;;
      "Debian"|"Devuan"|"Ubuntu")
          case "${LSB_VER}" in
            "Ubuntu16.04")
                # Update expired OpenVPN repository key (if needed)
                if apt-key list | grep -A 1 expired | grep -q '/E158C569' ; then
                    RUN_LOG "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys E158C569"
                fi
                ;;
            "Ubuntu18.04")
                for __f in "vivaldi" "vivaldi-stable" ; do
                    [ -e "/etc/apt/sources.list.d/${__f}.list" ] \
                        && RM_FILES "/etc/apt/sources.list.d/${__f}.list"
                done
                ;;
          esac
          ${__run_cmd} "apt-get -q -y update"
          ${__run_cmd} "apt-get upgrade -s | grep -e '^Inst' | awk '{print \$2\" \"\$4}' | tr -d '(' \
                > '${TMP_DIR}/upgradable-pkgs.list'" \
             || RM_FILES "${TMP_DIR}/upgradable-pkgs.list"
          ;;
    esac
#    JOB_END "Updating list of upgradable standard packages"

    # Prepare environment
    _prep_env

    # Create `standard` user account and prepare SSH (PKI, etc)
    _create_std_user
    VALIDATE_USER
    _prep_ssh
    _prep_openvpn

    # Remove "wrong" applications
    for __pkg in \
        "gnome-screensaver" \
        "xscreensaver" \
    ; do
        ${PKG_CHECK} ${__pkg}  && ${PKG_REMOVE} ${__pkg}
    done

    # Update application(s)
    #
    # -- /usr/local/bin/*
    for app in \
        "changeseedboxpass" \
        "install-app" \
        "_svc" \
        "seedbox-cp" \
        "easy-ssl" \
        "easy-ssl-v2" \
        "easy-ssl-nginx" \
        "ssl-mgr" \
        "plexdrive-init" \
        "reset-rsb-apache" \
        "relocate-app-data" \
        "speedtest-cli" \
        "bench.sh" \
        "set-ratio" \
        "rsb-iperf" \
        "rsb-mtr" \
        "rsb-ps-top" \
        "owasp-check" \
    ; do
        case "${app}" in
          "install-app")
              [ "${INSTALLAPP_UPDATE_CHECK}" != "yes" ] \
                  && continue
              ;;
        esac
#        L_LOG_START
#        INFO "Checking if '${app}' update available ..."
#        L_LOG
        WGET --nolog "${SUPPORT}/rsb-scripts/common/${app}"
        if [ $? -eq 0 ] ; then
            if [ -s "${TMP_DIR}/${app}" ] ; then
                ${__run_cmd} "diff '${TMP_DIR}/${app}' '/usr/local/bin/${app}'"
                if [ $? -ne 0 ] ; then
                    INFO "Updating '${app}' ..."
                    MV "${TMP_DIR}/${app}" "/usr/local/bin/${app}"
                    CHOWN_FILE root:root "/usr/local/bin/${app}"
                    CHMOD_FILE 755 "/usr/local/bin/${app}"
                    grep -q -E "^rsb-" <<< "${app}" 2>&1 \
                        || LN "/usr/local/bin/${app}" "/usr/local/bin/rsb-${app}"
#                else
#                    INFO "'${app}' is up to date."
                fi
            fi
        fi
#        L_LOG
#        L_LOG_FINISH
    done
    # -- /usr/local/lib/*
    for app in \
        "functions.sh" \
    ; do
#        L_LOG_START
#        INFO "Checking if '/usr/local/lib/rsb/${app}' update available ..."
#        L_LOG
        WGET --nolog "${SUPPORT}/rsb-scripts/lib/${app}"
        if [ $? -eq 0 ] ; then
            if [ -s "${TMP_DIR}/${app}" ] ; then
                ${__run_cmd} "diff '${TMP_DIR}/${app}' '/usr/local/lib/rsb/${app}'"
                if [ $? -ne 0 ] ; then
                    INFO "Updating '${app}' ..."
                    MV "${TMP_DIR}/${app}" "/usr/local/lib/rsb/${app}"
                    CHOWN_FILE root:root "/usr/local/lib/rsb/${app}"
#                else
#                    INFO "'/usr/local/lib/rsb/${app}' is up to date."
                fi
            fi
        fi
#        L_LOG
#        L_LOG_FINISH
    done
    # -- /usr/local/sbin/*
    for app in \
        "rsb-autoupdate.sh" \
    ; do
#        L_LOG_START
#        INFO "Checking if '${app}' update available ..."
#        L_LOG
        WGET --nolog "${SUPPORT}/rsb-scripts/sbin/${app}"
        if [ $? -eq 0 ] ; then
            if [ -s "${TMP_DIR}/${app}" ] ; then
                ${__run_cmd} "diff '${TMP_DIR}/${app}' '/usr/local/sbin/${app}'"
                if [ $? -ne 0 ] ; then
                    INFO "Updating '${app}' ..."
                    MV "${TMP_DIR}/${app}" "/usr/local/sbin/${app}"
                    CHOWN_FILE root:root "/usr/local/sbin/${app}"
#                else
#                    INFO "'${app}' is up to date."
                fi
            fi
        fi
#        L_LOG
#        L_LOG_FINISH
    done
    # -- /etc/cron.d/*
    if [ -d "/etc/cron.d" ] ; then
        _install_rsb_autoupdate_cronjob="no"
        _rsb_autoupdate_cronjob_ver="1.0.2"
        if [ ! -e "/etc/cron.d/rsb-autoupdate" ] ; then
            _install_rsb_autoupdate_cronjob="yes"
        else
            if [ "$( grep -m1 "# Version: " "/etc/cron.d/rsb-autoupdate" \
                         | awk '{print $NF}' )" != "${_rsb_autoupdate_cronjob_ver}" \
               ]
            then
                _install_rsb_autoupdate_cronjob="yes"
            fi
        fi
        if [ "${_install_rsb_autoupdate_cronjob}" = "yes" ] ; then
            INFO "Updating '/etc/cron.d/rsb-autoupdate' to version ${_rsb_autoupdate_cronjob_ver}"
            cat <<-EOF > "/etc/cron.d/rsb-autoupdate"
		# Version: ${_rsb_autoupdate_cronjob_ver}
		MAILTO=""
		PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

		10 0 * * * root test -e /var/local/.rsb-autoupdate && flock -w 3 /var/lock/rsb-autoupdate.lock /bin/bash /usr/local/sbin/rsb-autoupdate.sh >> /var/log/install-app.log
		EOF
        fi
    fi
    for app in \
        "rsb-autoupdate.sh" \
    ; do
#        L_LOG_START
#        INFO "Checking if '${app}' update available ..."
#        L_LOG
        WGET --nolog "${SUPPORT}/rsb-scripts/sbin/${app}"
        if [ $? -eq 0 ] ; then
            if [ -s "${TMP_DIR}/${app}" ] ; then
                ${__run_cmd} "diff '${TMP_DIR}/${app}' '/usr/local/sbin/${app}'"
                if [ $? -ne 0 ] ; then
                    INFO "Updating '${app}' ..."
                    MV "${TMP_DIR}/${app}" "/usr/local/sbin/${app}"
                    CHOWN_FILE root:root "/usr/local/bin/${app}"
#                else
#                    INFO "'${app}' is up to date."
                fi
            fi
        fi
#        L_LOG
#        L_LOG_FINISH
    done
    # Update `pure-ftpd` configs/scripts
    if [ -e "/etc/pure-ftpd" ] ; then
        for app in "changeftppass" "delftpuser" "ftpmgr" ; do
#            L_LOG_START
#            INFO "Checking if '${app}' update available ..."
#            L_LOG
            WGET --nolog "${SUPPORT}/rsb-scripts/common/${app}"
            if [ $? -eq 0 ] ; then
                if [ -s "${TMP_DIR}/${app}" ] ; then
                    ${__run_cmd} "diff '${TMP_DIR}/${app}' '/usr/local/bin/${app}'"
                    if [ $? -ne 0 ] ; then
                        INFO "Updating '${app}' ..."
                        MV "${TMP_DIR}/${app}" "/usr/local/bin/${app}"
                        CHOWN_FILE root:root "/usr/local/bin/${app}"
                        CHMOD_FILE 755 "/usr/local/bin/${app}"
                        LN "/usr/local/bin/${app}" "/usr/local/bin/rsb-${app}"
#                    else
#                        INFO "'${app}' is up to date."
                    fi
                fi
            fi
#            L_LOG
#            L_LOG_FINISH
        done
    fi
    # Update `plexmediaserver` configs/scripts
    if id plex > /dev/null 2>&1 ; then
        for app in \
          "claim-plex" \
          "reset-plex" \
          "relocate-plex" \
        ; do
#            L_LOG_START
#            INFO "Checking if '${app}' update available ..."
#            L_LOG
            if WGET --nolog "${SUPPORT}/rsb-scripts/common/${app}" ; then
                if [ -s "${TMP_DIR}/${app}" ] ; then
                    ${__run_cmd} "diff '${TMP_DIR}/${app}' '/usr/local/bin/${app}'"
                    if [ $? -ne 0 ] ; then
                        INFO "Updating '${app}' ..."
                        MV "${TMP_DIR}/${app}" "/usr/local/bin/${app}"
                        CHOWN_FILE root:root "/usr/local/bin/${app}"
                        CHMOD_FILE 755 "/usr/local/bin/${app}"
                        LN "/usr/local/bin/${app}" "/usr/local/bin/rsb-${app}"
#                    else
#                        INFO "'${app}' is up to date."
                    fi
                fi
            fi
#            L_LOG
#            L_LOG_FINISH
        done
        # -- Allow `plex` group & user account to write to user's Downloads directory
#        RUN_LOG "setfacl -Rm g:plex:rwX,d:g:plex:rwX /etc/skel/Downloads   2>&1"
#        RUN_LOG "setfacl -Rm g:plex:rwX,d:g:plex:rwX /home/user/Downloads  2>&1"
#        RUN_LOG "setfacl -Rm g:plex:rwX,d:g:plex:rwX ${USERHOME}/Downloads 2>&1"
        ${__run_cmd} "setfacl -Rm g:plex:rwX,d:g:plex:rwX /etc/skel/Downloads   2>&1"
        ${__run_cmd} "setfacl -Rm g:plex:rwX,d:g:plex:rwX /home/user/Downloads  2>&1"
        ${__run_cmd} "setfacl -Rm g:plex:rwX,d:g:plex:rwX ${USERHOME}/Downloads 2>&1"
    fi

    # Prepare Apache2 if needed
    if [ -d "${APACHE_CONF_DIR}" ] || [ "${USE_APACHE2}" = "yes" ] ; then
        if [ "${LSB_VENDOR}" = "Debian" -o "${LSB_VENDOR}" = "Ubuntu" ] ; then
            _prep_apache2_deb
        elif [ "${LSB_VENDOR}" = "CentOS" ] ; then
            _prep_apache2_centos
        elif [ "${LSB_VENDOR}" = "CentOSStream" ] ; then
            _prep_apache2_centos
        fi
        # Modify user group membership
#        RUN_LOG "usermod -a -G ${APACHE_GROUP} ${USERNAME}"
        ${__run_cmd} "usermod -a -G ${APACHE_GROUP} ${USERNAME}"

        # Allow apache group to write to user's Download directory
#        RUN_LOG "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX /etc/skel/Downloads   2>&1"
#        RUN_LOG "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX /home/user/Downloads  2>&1"
#        RUN_LOG "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX ${USERHOME}/Downloads 2>&1"
        ${__run_cmd} "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX /etc/skel/Downloads   2>&1"
        ${__run_cmd} "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX /home/user/Downloads  2>&1"
        ${__run_cmd} "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX ${USERHOME}/Downloads 2>&1"
    fi

    if [ -d "/var/www" ] ; then
        # seedbox-cp-qmgr
        _restart_qmgr="no"
        if [ ! -d "/var/www/spool/seedbox-cp-qmgr" ] ; then
            MKDIR "/var/www/spool/seedbox-cp-qmgr"
            CHMOD_DIR 755 "/var/www/spool/seedbox-cp-qmgr"
            CHOWN_DIR "${APACHE_USER}":"${APACHE_GROUP}" "/var/www/spool/seedbox-cp-qmgr"
        fi
        for __d in "IN" "OUT" ; do
            if [ ! -d "/var/www/spool/seedbox-cp-qmgr/${__d}" ] ; then
                MKDIR "/var/www/spool/seedbox-cp-qmgr/${__d}"
                CHMOD_DIR 755 "/var/www/spool/seedbox-cp-qmgr/${__d}"
                CHOWN_DIR "${APACHE_USER}":"${APACHE_GROUP}" "/var/www/spool/seedbox-cp-qmgr/${__d}"
            fi
        done
        [ ! -d "/usr/local/sbin" ] && MKDIR "/usr/local/sbin"
#        L_LOG_START
#        INFO "Checking if 'seedbox-cp-qmgr.sh' update available ..."
#        L_LOG
        if WGET --nolog "${SUPPORT}/rsb-scripts/sbin/seedbox-cp-qmgr.sh" ; then
            if [ -s "${TMP_DIR}/seedbox-cp-qmgr.sh" ] ; then
                if ! ${__run_cmd} "diff '${TMP_DIR}/seedbox-cp-qmgr.sh' '/usr/local/sbin/seedbox-cp-qmgr.sh'"
                then
                    INFO "Updating '/usr/local/sbin/seedbox-cp-qmgr.sh' ..."
                    MV "${TMP_DIR}/seedbox-cp-qmgr.sh" "/usr/local/sbin/seedbox-cp-qmgr.sh"
                    CHOWN_FILE root:root "/usr/local/sbin/seedbox-cp-qmgr.sh"
                    CHMOD_FILE 700 "/usr/local/sbin/seedbox-cp-qmgr.sh"
                    _restart_qmgr="yes"
#                else
#                    INFO "'/usr/local/sbin/seedbox-cp-qmgr.sh' is up to date."
                fi
            fi
        fi
        if [ -d "/etc/systemd/system" ] ; then
            _systemd_reload="no"
            if [ -e "/etc/systemd/system/seedbox-cp-qmgr.path" ] ; then
                SVC_STOP  "seedbox-cp-qmgr.path"
                systemctl is-enabled "seedbox-cp-qmgr.path" > /dev/null 2>&1 \
                    || SVC_DISABLE  "seedbox-cp-qmgr.path"
                RM_FILES "/etc/systemd/system/seedbox-cp-qmgr.path"
                _systemd_reload="yes"
            fi
#            L_LOG_START
#            INFO "Checking if 'seedbox-cp-qmgr.service' update available ..."
#            L_LOG
            if WGET --nolog "${SUPPORT}/rsb-scripts/systemd/seedbox-cp-qmgr.service" ; then
                if ! ${__run_cmd} \
                      "diff '${TMP_DIR}/seedbox-cp-qmgr.service' '/etc/systemd/system/seedbox-cp-qmgr.service'"
                then
                    INFO "Updating '/etc/systemd/system/seedbox-cp-qmgr.service' ..."
                    MV "${TMP_DIR}/seedbox-cp-qmgr.service" \
                       "/etc/systemd/system/seedbox-cp-qmgr.service"
                    _systemd_reload="yes"
                    _restart_qmgr="yes"
#                else
#                    INFO "'/etc/systemd/system/seedbox-cp-qmgr.service' is up to date."
                fi
            fi
            if [ "${_systemd_reload}" = "yes" ] ; then
                SYSTEMD_RELOAD
                SVC_ENABLE "seedbox-cp-qmgr"
            fi
            if [ "${_restart_qmgr}" = "yes" ] ; then
                SVC_RESTART "seedbox-cp-qmgr"
            fi
        fi

        # Cleanup (old stuff)
        for __f in "cmd" "cmd.lock" "out" "out.err" ; do
            if [ -e "/var/www/spool/seedbox-cp-qmgr/${__f}" ] ; then
                RM_FILES "/var/www/spool/seedbox-cp-qmgr/${__f}"
            fi
        done
    fi


    # 'Fix' logrotate files modes - we have a bug in the template :|
    [ -d "/etc/logrotate.d" ] \
        && __run_nolog "chmod 644 /etc/logrotate.d/*"

    # Disable/stop harmful services
    #
    # -- lightdm
    systemctl is-enabled "lightdm" > /dev/null 2>&1 \
        && SVC_DISABLE "lightdm"
    systemctl is-active "lightdm" > /dev/null 2>&1 \
        && SVC_STOP "lightdm"

}




# ----------------------------------------------------------
# Application installation checks functions
# ----------------------------------------------------------

# Check if it safe to install/uninstall application.
# Application name is passed via $1 or determined by $APP content.
# Returns:  0     - if it is safe to install/uninstall application.
#           1     - if required parameter(s) missing.
#           111   - if app is installed and installation requested.
#           112   - if app is not installed and uninstallation was requested.
#           113   - Application is NOT supported on current OS.
#           114   - Another installation procedure is in progress.
#           115   - System uptime is too low ( < 10 minutes ).
#           116   - Resources shortage (f.e. low VPS total memory, not enough free disk space, etc).
#           117   - Application conflicts with one (or more) already installed application(s).
#
function CHECK_APP_DEB () {
#    [ "${DEBUG}" = "yes" ] && ENTRY

    local app
    local o_app
    local script_name
    local op
    local op_RC
    local _pkg
    local _rc

    script_name="$( basename "${0}" )"
    LOG=""
    _rc=0

    if [ -n "${2}" ] ; then
        op="${2}"
    else
        case ${script_name} in
        install_*)
            op="install"
            ;;
        uninstall_*)
            op="uninstall"
            ;;
        upgrade_*)
            op="upgrade"
            ;;
        __*)
            op="maintenance"
            ;;
        *)
            ERROR "CHECK_APP(): Invalid script name - only install_*.sh/uninstall_*.sh/upgrade_*.sh/__*.sh names allowed!"
            _rc=1
            return ${_rc}
            ;;
        esac
    fi

    o_app="${1}"
    [ -z "${o_app}" ] && o_app="${APP}"
    if [ -z "${o_app}" ] ; then
        ERROR "CHECK_APP(): Argument(s) missing"
        _rc=1
        return ${_rc}
    fi

#    app="$( echo "${o_app}" | tr '[:upper:]' '[:lower:]' )"
    app="${o_app,,}"

    case "${app}" in

    # Check dpkg package status
    "ampache" \
     |"aria2" \
     |"dropbox" \
     |"filezilla" \
     |"flareget" \
     |"handbrake" \
     |"madsonic" \
     |"megasync" \
     |"midori" \
     |"mkvtoolnix" \
     |"monitorix" \
     |"neofetch" \
     |"nicotine" \
     |"nomachine" \
     |"openbox" \
     |"pidgin" \
     |"pydio" \
     |"qbittorrent" \
     |"qupzilla" \
     |"softethervpn" \
     |"subsonic" \
     |"tixati" \
     |"tribler" \
     |"uget" \
     |"vlc" \
     |"waterfox" \
     |"xrdp" \
     )
        _rc=0
        _pkg="${app}"
        [ -n "${SYS_PKG_NAME}" ] && _pkg="${SYS_PKG_NAME}"
        if DPKG_CHECK "${_pkg}" ; then
            APP_VER="$( ${PKG_VER} "${_pkg}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    # Check ${APP_DIRECTORY} existence
    "btsync" \
     |"couchpotato" \
     |"cuberite" \
     |"deluge (latest)" \
     |"deluge 1.3.5" \
     |"deluge 1.3.6" \
     |"deluge 1.3.7" \
     |"deluge 1.3.8" \
     |"deluge 1.3.9" \
     |"deluge 1.3.10" \
     |"deluge 1.3.11" \
     |"deluge 1.3.12" \
     |"deluge 1.3.13" \
     |"deluge 1.3.14" \
     |"deluge 2" \
     |"elfinder" \
     |"filecommander" \
     |"filerun" \
     |"headphones" \
     |"jackett" \
     |"jdownloader 2" \
     |"nzbget" \
     |"proxy puppeteer" \
     |"pyload" \
     |"rapidleech" \
     |"rsb dashboard" \
     |"rsb wiztorrent" \
     |"sabnzbd" \
     |"sickbeard" \
     |"utorrent" \
     |"vinaget" \
     |"vuetorrent ui" \
     )
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            case "${app}" in
              deluge*)
                  if [ ! -z "${DELUGE_VER}" ] ; then
                      APP_VER="${DELUGE_VER}"
                      if [ -d "${APP_DIRECTORY}" ] ; then
                          [ "${op}" = "install" ]   && _rc=111
                      else
                          [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                      fi
                  else
                      if [ "${app}" = "deluge (latest)" -o "${app}" = "deluge 2" ] ;then
                          if DPKG_CHECK "deluged" ; then
                              APP_VER="$( ${PKG_VER} "deluged" )"
                              if [ -d "${APP_DIRECTORY}" ] ; then
                                  [ "${op}" = "install" ]   && _rc=111
                              else
                                  [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                              fi
                          fi
                      fi
                  fi
                  ;;
              *)
                  if [ -d "${APP_DIRECTORY}" ] ; then
                      [ "${op}" = "install" ]   && _rc=111
                  else
                      [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                  fi
                  ;;
            esac
        fi
        ;;

    # Check "non-standard" apps :)
    "7zip")
        _rc=0
        if DPKG_CHECK "p7zip-full" ; then
            APP_VER="$( ${PKG_VER} "p7zip-full" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "ariang")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -s "${APP_DIRECTORY}/version.id" ] ; then
                    APP_VER="$( cat "${APP_DIRECTORY}/version.id" )"
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "autobrr")
        _rc=0
        if [ -x "${APP_HOME}/autobrr" ] && [ -e "${USERHOME}/.config/autobrr/config.toml" ]
        then
            if [ -e "${USERHOME}/.config/autobrr/config.toml" ] ; then
                [ "${op}" = "install" ] && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "basilisk")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                APP_VER="$( grep -m 1 -e '^Version=.*$' "${APP_DIRECTORY}/application.ini" \
                                  | sed -e 's/^Version=\(.*\)$/\1/' \
                                  | awk -F. '{print $(NF-2)"."$(NF-1)"."$NF}' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "bat")
        _rc=0
        if DPKG_CHECK "bat-musl" ; then
            APP_VER="$( ${PKG_VER} "bat-musl" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "bazarr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            APP_HOME="/opt/bazarr"
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_HOME}/version.id" ]
                then
                    APP_VER="$( cat "${APP_HOME}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "brave")
        _rc=0
        if DPKG_CHECK "brave-browser" ; then
            APP_VER="$( ${PKG_VER} "brave-browser" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "calibre")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                APP_VER="$( calibre --version 2> /dev/null | awk '{print $NF}' | tr -d ')' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "cherrymusic")
        _rc=0
        test -x "/usr/local/bin/cherrymusic"
        if [ $? -eq 0 ] ; then
#            APP_VER="$( cherrymusic --version 2> /dev/null | head -1 | awk '{print $NF}' )"
            APP_VER="$( PIPX_VER "cherrymusic" )"
            if [    -d "${USERHOME}/.config/cherrymusic" \
                 -o -d "${USERHOME}/.local/share/cherrymusic" ]
            then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade"  ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "cinnamon desktop")
        _rc=0
        if DPKG_CHECK "cinnamon" ; then
            APP_VER="$( ${PKG_VER} "cinnamon" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "cloudcmd")
        _rc=0
        if which cloudcmd > /dev/null && test -e "${USERHOME}/.cloudcmd.json"
        then
            APP_VER="$( cloudcmd -v | tr -d 'v' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade"  ] && _rc=112
        fi
        ;;

    "cloud torrent")
        _rc=0
        if DPKG_CHECK "cloud-torrent" ; then
            APP_VER="$( ${PKG_VER} "cloud-torrent" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "deluge - dropbox upload")
        _rc=0
        ;;

    "deluge - google drive upload")
        _rc=0
        ;;

    "deluge - streaming plugin")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [    -d "${APP_DIRECTORY}" \
                 -a -e "${APP_DIRECTORY}/enable_streaming_plugin.py" \
                 -a -e "${APP_DIRECTORY}/disable_streaming_plugin.py" ]
            then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "docker")
        _rc=0
        case "${LSB_VER}" in
          "Debian9")
              _pkg="docker" ;;
          *)
              _pkg="docker.io" ;;
        esac
        if DPKG_CHECK "${_pkg}" ; then
            APP_VER="$( ${PKG_VER} "${_pkg}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "dropbox cli")
        _rc=0
        if [ -e "${APP_DIRECTORY}/dropbox_uploader.sh" ] ; then
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "emby server")
        _rc=0
        if DPKG_CHECK "emby-server" ; then
            APP_VER="$( ${PKG_VER} "emby-server" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "filebrowser")
        _rc=0
        if [ -x "${APP_DIRECTORY}/filebrowser" ] ; then
            if [ -e "${APP_DIRECTORY}/version.id" ] ; then
                if [ -e "${APP_DIRECTORY}/version.id" ] ; then
                    APP_VER="$( < "${APP_DIRECTORY}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "firefox")
        # Firefox
        case "${LSB_VENDOR}" in
          "Debian"|"Devuan")
            PKG="firefox-esr"
            ;;
          *)
            case "${LSB_VER}" in
              "Ubuntu22.04")
                  PKG="firefox-esr"
                  ;;
              *)
                  PKG="firefox"
                  ;;
            esac
            ;;
        esac
        if DPKG_CHECK "${PKG}" ; then
            APP_VER="$( ${PKG_VER} "${PKG}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "fixes for cuteftp users")
        _rc=0
        ;;

    "fixes for kodi sftp connection")
        _rc=0
        ;;

    "fixes for rutorrent fileshare plugin")
        _rc=0
        ;;

    "flatpak")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if DPKG_CHECK "flatpak" ; then
                if [ -d "${APP_DIRECTORY}" ] ; then
                    APP_VER="$( ${PKG_VER} "flatpak" )"
                    [ "${op}" = "install" ]   && _rc=111
                else
                    [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                fi
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "flexget")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                which flexget > /dev/null 2>&1 \
                    && APP_VER="$( flexget --version 2> /dev/null | head -1 )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "flood")
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
                GIT_DIR="${APP_DIRECTORY}/.git"
                APP_VER="$( GIT_DIR="${GIT_DIR}" git describe --tags \
                               | awk -F "/" '{print $NF}' \
                               | sed "s/\([^-]*\)-.*/\1/" )"
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "floorp")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -s "${APP_DIRECTORY}/version.id" ] ; then
                    APP_VER="$( cat "${APP_DIRECTORY}/version.id" )"
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "gnome desktop")
        _rc=0
        if DPKG_CHECK "ubuntu-desktop" ; then
            APP_VER="$( ${PKG_VER} "ubuntu-desktop" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "google chrome")
        _rc=0
        if DPKG_CHECK "google-chrome-stable" ; then
            APP_VER="$( ${PKG_VER} "google-chrome-stable" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "google drive cli")
        _rc=0
        if [ -e "${APP_DIRECTORY}/gdrive" ] ; then
            APP_VER="$( "${APP_DIRECTORY}/gdrive" version \
                           | grep -m 1 -e '^gdrive:' \
                           | awk '{print $2}' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "icecat")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                APP_VER="$( grep -m 1 -e '^Milestone=.*$' "${APP_DIRECTORY}/platform.ini" \
                                  | sed -e 's/^Milestone=\(.*\)$/\1/' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "icewm desktop")
        _rc=0
        if DPKG_CHECK "icewm" ; then
            APP_VER="$( ${PKG_VER} "icewm" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "i2p daemon")
        _rc=0
        if DPKG_CHECK "i2pd" ; then
            APP_VER="$( ${PKG_VER} "i2pd" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "jdownloader")
        _rc=0
        if DPKG_CHECK "jdownloader-installer" ; then
            APP_VER="$( ${PKG_VER} "jdownloader-installer" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "jdownloader 2")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
#                APP_VER="${SCRIPT_VER}"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "jellyfin")
        _rc=0
        if DPKG_CHECK "jellyfin" ; then
            APP_VER="$( ${PKG_VER} "jellyfin" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "kde desktop")
        _rc=0
        if DPKG_CHECK "kubuntu-desktop" ; then
            APP_VER="$( ${PKG_VER} "kubuntu-desktop" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "l2tp-ipsec vpn (racoon)")
        _rc=0
        if [ ! -c "/dev/ppp" ] ; then
            ERROR "Device '/dev/ppp' not found!"
            _rc=1
        else
            if DPKG_CHECK "racoon" ; then
                APP_VER="$( ${PKG_VER} "racoon" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "l2tp-ipsec vpn (swan)")
        _rc=0
        if [ ! -c "/dev/ppp" ] ; then
            ERROR "Device '/dev/ppp' not found!"
            _rc=1
        else
            if DPKG_CHECK "strongswan" ; then
                APP_VER="$( ${PKG_VER} "strongswan" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "librespeed")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_DIRECTORY}/version.id" ]
                then
                    APP_VER="$( cat "${APP_DIRECTORY}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "lidarr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            APP_HOME="/opt/Lidarr"
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_HOME}/version.id" ]
                then
                    APP_VER="$( cat "${APP_HOME}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "lumina desktop")
        _rc=0
        if DPKG_CHECK "lumina-desktop" ; then
            APP_VER="$( ${PKG_VER} "lumina-desktop" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "lxde desktop")
        _rc=0
        if DPKG_CHECK "lxde" ; then
            APP_VER="$( ${PKG_VER} "lxde" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "lxqt desktop")
        _rc=0
        if DPKG_CHECK "lxqt" ; then
            APP_VER="$( ${PKG_VER} "lxqt" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "mate desktop")
        _rc=0
        if DPKG_CHECK "mate-utils-common" ; then
            APP_VER="$( ${PKG_VER} "mate-utils-common" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "microsoft edge")
        _rc=0
        if DPKG_CHECK "microsoft-edge-dev" ; then
            APP_VER="$( ${PKG_VER} "microsoft-edge-dev" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "min")
        _rc=0
        if DPKG_CHECK "min" ; then
            APP_VER="$( ${PKG_VER} "min" | awk -F '-' '{print $1}' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "mullvad browser")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_DIRECTORY}/version.id" ]
                then
                    APP_VER="$( cat "${APP_DIRECTORY}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "netdata")
        _rc=0
        if [ -e "/usr/sbin/netdata" ] ; then
            APP_VER="$( /usr/sbin/netdata -v | awk '{print $2}' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "nextcloud server")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -e "${APP_DIRECTORY}" ] && [ -d "$( readlink -f "${APP_DIRECTORY}" )" ] ; then
                APP_VER="$( grep -m 1 -e "^\$OC_VersionString.*" "${APP_DIRECTORY}/version.php" \
                               | sed -e "s/.*'\(.*\)'.*/\1/" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "nzbgetcom")
        _rc=0
        if [ -x /opt/nzbgetcom/nzbget ] > /dev/null 2>&1 ; then
            APP_VER="$( /opt/nzbgetcom/nzbget --version | awk "{print \$NF}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "nzbhydra 2")
        _rc=0
        if [ -e "/opt/nzbhydra2/version.id" ] && [ -d "${USERHOME}/.config/nzbhydra" ]
        then
            [ "${op}" = "install" ] && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "ombi")
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
#                APP_VER="$( grep -m1 -E "Ombi/.*-master" "${APP_DIRECTORY}/Ombi.deps.json" \
#                                | sed -e "s#^[^/]*/\(.*\)-master.*#\1#" )"
                APP_VER="$( "${APP_DIRECTORY}/Ombi" --version 2>&1 | head -1 | awk '{print $NF}' )"
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "openbox desktop")
        _rc=0
        if DPKG_CHECK "openbox" ; then
            APP_VER="$( ${PKG_VER} "openbox" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "openvpn 2.4")
        _rc=0
        if DPKG_CHECK "openvpn" ; then
            if dpkg -s openvpn | grep -m 1 -q -e '^Version:\s*2\.4\..*' > /dev/null 2>&1 ; then
                APP_VER="$( ${PKG_VER} "openvpn" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "openvpn 2.5")
        _rc=0
        if DPKG_CHECK "openvpn" ; then
            if dpkg -s openvpn | grep -m 1 -q -e '^Version:\s*2\.5\..*' > /dev/null 2>&1 ; then
                APP_VER="$( ${PKG_VER} "openvpn" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "openvpn 2.6")
        _rc=0
        if DPKG_CHECK "openvpn" ; then
            if dpkg -s openvpn | grep -m 1 -q -e '^Version:\s*2\.6\..*' > /dev/null 2>&1 ; then
                APP_VER="$( ${PKG_VER} "openvpn" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "opera")
        _rc=0
        if DPKG_CHECK "opera-stable" ; then
            APP_VER="$( ${PKG_VER} "opera-stable" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "oracle java jre 8")
        _rc=0
        if DPKG_CHECK "oracle-java8-jre" ; then
            APP_VER="$( ${PKG_VER} "oracle-java8-jre" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "organizr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                APP_VER="$( grep -B7 -E '^}' "${APP_DIRECTORY}/js/version.json" \
                              | head -1 \
                              | sed -e 's/^\s*"\(.*\)".*$/\1/' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "os update")
        APP_VER="$( lsb_release -sd )"
        _rc=0
        ;;

    "overseerr")
        _rc=0
        if which snap > /dev/null 2>&1 ; then
            if snap list overseerr > /dev/null 2>&1 ; then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "owncloud server")
        _rc=0
        if DPKG_CHECK "owncloud-files" ; then
            APP_VER="$( ${PKG_VER} "owncloud-files" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "pale moon")
        _rc=0
        if DPKG_CHECK "palemoon" ; then
            APP_VER="$( ${PKG_VER} "palemoon" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "persepolis download manager")
        _rc=0
        if DPKG_CHECK "persepolis" ; then
            APP_VER="$( ${PKG_VER} "persepolis" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "plex")
        _rc=0
        if DPKG_CHECK "plexmediaserver" ; then
            APP_VER="$( ${PKG_VER} "plexmediaserver" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "plex webtools")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
            _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                case "${op}" in
                  "install")
                      _rc=111
                      ;;
                  "upgrade")
                      _check_mem || _rc=116
                      ;;
                esac
            else
                case "${op}" in
                  "install")
                      _check_mem || _rc=116
                      ;;
                  "uninstall"|"upgrade")
                      _rc=112
                      ;;
                esac
            fi
        fi
        ;;

    "plexdrive service")
        _rc=0
        if [ -z "${APP_HOME}" ] ; then
            LOG="CHECK_APP(): \$APP_HOME is not defined!"
            _rc=1
        else
            if [    -x "/usr/local/bin/plexdrive" \
                 -a -e "/etc/systemd/system/plexdrive.service" ]
            then
                case "${op}" in
                  "install")
                      _rc=111
                      ;;
                  "upgrade")
                      _check_mem || _rc=116
                      ;;
                esac
            else
                case "${op}" in
                  "install")
                      _check_mem || _rc=116
                      ;;
                  "uninstall"|"upgrade")
                      _rc=112
                      ;;
                esac
            fi
        fi
        ;;

     "plexpy"|"plex trakt scrobbler"|"tautulli")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
            _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                case "${op}" in
                  "install")
                      _rc=111
                      ;;
                  "upgrade")
                      _check_mem || _rc=116
                      ;;
                esac
            else
                case "${op}" in
                  "install")
                      _check_mem || _rc=116
                      ;;
                  "uninstall"|"upgrade")
                      _rc=112
                      ;;
                esac
            fi
        fi
        ;;

    "prowlarr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            APP_HOME="/opt/Prowlarr"
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_HOME}/version.id" ]
                then
                    APP_VER="$( cat "${APP_HOME}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "pure ftpd")
        _rc=0
        if DPKG_CHECK "pure-ftpd" ; then
            APP_VER="$( ${PKG_VER} "pure-ftpd" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "qbittorrent webui")
        _rc=0
        if DPKG_CHECK "qbittorrent-nox" ; then
            APP_VER="$( ${PKG_VER} "qbittorrent-nox" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "qbittorrent webui (latest)")
        _rc=0
        if [ -e "/usr/local/qbittorrent-nox-static/qbittorrent-nox" ] ; then
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "/usr/local/qbittorrent-nox-static/version.id" ] ; then
                    APP_VER="$( < "/usr/local/qbittorrent-nox-static/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "radarr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            APP_HOME="/opt/Radarr"
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_HOME}/version.id" ]
                then
                    APP_VER="$( cat "${APP_HOME}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "rclone browser")
        _rc=0
        if DPKG_CHECK "rclone-browser" ; then
            APP_VER="$( ${PKG_VER} "rclone-browser" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;


    "rclone webui")
        _rc=0
        if    [ -x "/usr/local/bin/rclone" ] \
           && [ -e "/etc/systemd/system/rclonewebui@.service" ] ; then
            APP_VER="$( /usr/local/bin/rclone --version | head -1 | awk "{print \$NF}" | tr -d "v" )"
            #
            [ "${op}" = "install" ] && _rc=111
        else
            [ "${op}" = "uninstall" ] || [ "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "readarr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            APP_HOME="/opt/Readarr"
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_HOME}/version.id" ]
                then
                    APP_VER="$( cat "${APP_HOME}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "rebuild db")
        _rc=0
        ;;

    "resilio sync")
        _rc=0
        if DPKG_CHECK "resilio-sync" ; then
            APP_VER="$( ${PKG_VER} "resilio-sync" )"
            if [ -z "${APP_DIRECTORY}" ] ; then
                LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
               _rc=1
            else
                if [ -d "${APP_DIRECTORY}" ] ; then
                    [ "${op}" = "install" ]   && _rc=111
                else
                    [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                fi
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "rinetd bbr")
        _rc=0
        if [    -x "/usr/local/sbin/rinetd-bbr" \
             -a -e "/etc/systemd/system/rinetd.service" ]
        then
            case "${op}" in
              "install")
                  _rc=111
                  ;;
            esac
        else
            case "${op}" in
              "uninstall")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "p3a client library")
        _rc=0
        if DPKG_CHECK "rsb-p3a-client" ; then
            APP_VER="$( ${PKG_VER} "rsb-p3a-client" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "rtorrent")
        _rc=0
        if ! DPKG_CHECK "rtorrent" ; then
            _rc=99
        elif ! grep -q -e \
              '^ExecStart=.*\s*/usr/bin/rtorrent' "/etc/systemd/system/rtorrent@.service" > /dev/null 2>&1
        then
            _rc=99
        elif [ ! -e "${USERHOME}/.rtorrent.rc" ] ; then
            _rc=99
        fi
        if [ ${_rc} -eq 0 ] ; then
            APP_VER="$( ${PKG_VER} "rtorrent" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            if [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] ; then
                _rc=112
            elif [ "${op}" = "install" ] ; then
                _rc=0
            fi
        fi
        ;;

    "rtorrent 0.9.8")
        _rc=0
        if [ ! -x "/usr/local/rt098/bin/rtorrent" ] ; then
            _rc=99
        elif ! grep -q -e \
              '^ExecStart=.*\s*/usr/local/rt098/bin/rtorrent' \
                   "/etc/systemd/system/rtorrent@.service" > /dev/null 2>&1
        then
            _rc=99
        elif [ ! -e "${USERHOME}/.rtorrent.rc" ] ; then
            _rc=99
        fi
        if [ ${_rc} -eq 0 ] ; then
            APP_VER="0.9.8"
            [ "${op}" = "install" ]   && _rc=111
        else
            if [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] ; then
                _rc=112
            elif [ "${op}" = "install" ] ; then
                _rc=0
            fi
        fi
        ;;

    "rutorrent (latest)" \
     |"rutorrent (rtorrent 0.9.2)" \
     |"rutorrent (rtorrent 0.9.3)" \
     |"rutorrent (rtorrent 0.9.4)" \
     |"rutorrent (rtorrent 0.9.6)" \
     |"rutorrent (rtorrent 0.9.8)" \
     |"rutorrent 4 ui" \
     |"rutorrent ui" \
     |"rutorrent" )
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}/conf/users/${USERNAME}" ] ; then
                if [ "${op}" = "install" ] ; then
                    if [ "${app}" = "rutorrent (latest)" ] ; then
                        if which rtorrent > /dev/null 2>&1 ; then
                            _rc=111
                        fi
                    else
                        _rc=111
                    fi
                fi
#                GIT_DIR="${APP_DIRECTORY}/.git"
#                if [ -d "${GIT_DIR}" ] ; then
#                    APP_VER="$( GIT_DIR="${GIT_DIR}" git describe --tags \
#                                   | awk -F "/" '{print $NF}' \
#                                   | sed "s/\(.*\)-.*/\1/" \
#                                   | tr "-" "_" )"
#                else
                    if [ -e "${APP_DIRECTORY}/version.id" ] ; then
                        APP_VER="$( < "${APP_DIRECTORY}/version.id" )"
                    else
                        APP_VER="$( grep -m 1 -E '\s+version:' "${APP_DIRECTORY}/js/webui.js" \
                                     | awk '{print $NF}' \
                                     | tr -d '",' )"
                    fi
#                fi
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "rutorrent - autodl plugin")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${RUTORRENT_DIRECTORY}" -a -d "${APP_DIRECTORY}" ] ; then
                __cfg="${RUTORRENT_DIRECTORY}/conf/users/${USERNAME}/config.php"
                if RUN_LOG "grep -q -e '\$autodlPort' '${__cfg}' 2>&1" && \
                   RUN_LOG "grep -q -e '\$autodlPassword' '${__cfg}' 2>&1"
                then
                    [ "${op}" = "install" ] && _rc=111
                else
                    [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                fi
            fi
        fi
        ;;

    "rutorrent - dropbox upload")
        _rc=0
        if [ -e "${USERHOME}/bin/upload-to-dropbox.sh" ] ; then
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "rutorrent - filebot")
        _rc=0
        ;;

    "rutorrent - google drive upload")
        _rc=0
        if [ -e "${USERHOME}/bin/upload-to-gdrive.sh" ] ; then
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "rutorrent - mediastream plugin")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "seedboxcp")
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -e "${APP_DIRECTORY}/config.php" ] ; then
                APP_VER="$( sed -n "s/^\$version = '\(.*\)';$/\1/p" "${APP_DIRECTORY}/config.php" )"
                [ "${op}" = "install" ] && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "sickchill")
        _rc=0
        if [ -e "${USERHOME}/.local/bin/SickChill" ] ; then
            APP_VER="$( sudo -u ${USERNAME} pip3 show sickchill | awk '$1 == "Version:" {print $NF}' )"
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "sickrage")
        _rc=0
        if [ -e "${APP_DIRECTORY}/SiCKRAGE.py" ] ; then
#            APP_VER="$( sudo -u ${USERNAME} pip3 show sickrage | awk '$1 == "Version:" {print $NF}' )"
            APP_VER="$( crudini --get "${APP_DIRECTORY}/setup.cfg" bumpversion current_version )"
#            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
#            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "simple torrent")
        _rc=0
        if [ -x "/usr/local/bin/simple-torrent" ] && [ -d "${APP_DIRECTORY}" ] ; then
            APP_VER="$( simple-torrent --version 2>&1 )"
            [ "${op}" = "install" ] && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade"  ] && _rc=112
        fi
        ;;

    "sonarr")
        _rc=0
        if DPKG_CHECK "sonarr" ; then
            APP_VER="$( ${PKG_VER} "sonarr" )"
            if [ -e "${USERHOME}/.config/sonarr/config.xml" ] ; then
                [ "${op}" = "install" ] && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "sonarr (latest)")
        _rc=0
        if [ -x "/opt/Sonarr-latest/Sonarr" ] && [ -e "${USERHOME}/.config/sonarr-latest/config.xml" ]
        then
            if [ -e "${USERHOME}/.config/sonarr-latest/config.xml" ] ; then
                [ "${op}" = "install" ] && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "storm")
        _rc=0
        if    [ -x "${APP_DIRECTORY}/storm" ] \
           && [ -x "/usr/local/sbin/storm-wrapper.sh" ] \
           && [ -e "/etc/systemd/system/storm@.service" ]
        then
            [ "${op}" = "install" ] && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "subliminal")
        _rc=0
        if PIP3_CHECK "subliminal" ; then
            APP_VER="$( pip3 show subliminal 2>/dev/null | grep -m 1 -E "^Version:" | awk '{print $2}' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "syncthing")
        _rc=0
        if DPKG_CHECK "syncthing" ; then
            APP_VER="$( ${PKG_VER} "syncthing" )"
            if [ -e "${USERHOME}/.config/syncthing/config.xml" ] ; then
                [ "${op}" = "install" ]   && _rc=111
            elif [ -e "/root/.config/syncthing/config.xml" ] ; then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "thinlinc")
        _rc=0
        if DPKG_CHECK "thinlinc-webaccess" ; then
            APP_VER="$( ${PKG_VER} "thinlinc-webaccess" )"
            [ "${op}" = "install" ]   && _rc=111
        elif DPKG_CHECK "thinlinc-server" ; then
            APP_VER="$( ${PKG_VER} "thinlinc-server" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "thunderbird")
        _pkg="thunderbird"
        [ "${LSB_VENDOR}" = "Debian" ] && _pkg="icedove"
        _rc=0
        if DPKG_CHECK "${_pkg}" ; then
            APP_VER="$( ${PKG_VER} "${_pkg}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "tor browser")
        _rc=0
        if DPKG_CHECK "torbrowser-launcher" ; then
            APP_VER="$( ${PKG_VER} "torbrowser-launcher" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "transmission")
        _rc=0
        if DPKG_CHECK "transmission-gtk" ; then
            APP_VER="$( ${PKG_VER} "transmission-gtk" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "transmission daemon")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                DPKG_CHECK "transmission-daemon" \
                   && APP_VER="$( ${PKG_VER} "transmission-daemon" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "transmission 4 daemon")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ -x "/usr/local/bin/transmission-daemon" ] \
                   && APP_VER="$( /usr/local/bin/transmission-daemon --version 2>&1 \
                                      | awk '{print $2}' )"
#                                      | awk '{print $2"-"$3}' | tr -d '()' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "ttyd")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if   [ -x "/usr/local/bin/ttyd" ] \
              && [ -x "/usr/local/bin/CLI" ] \
              && [ -e "/etc/systemd/system/ttyd.service" ]; then
                [ "${op}" = "install" ] && _rc=111
            else
                [ "${op}" = "uninstall" ] || [ "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "update rsb custom scripts")
        _rc=0
        ;;

    "update rsb custom scripts - test")
        _rc=0
        ;;

    "utorrent 2.2.1 (wine)")
        _rc=0
        if [ -e "${APP_DIRECTORY}/utorrent_2.2.1_build_25302.exe" ] ; then
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "vivaldi")
        _rc=0
        if DPKG_CHECK "vivaldi-stable" ; then
            APP_VER="$( ${PKG_VER} "vivaldi-stable" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

     "vnc")
        _rc=0
        if DPKG_CHECK "tightvncserver" ; then
            APP_VER="$( ${PKG_VER} "tightvncserver" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "webssh")
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
#                GIT_DIR="${APP_DIRECTORY}/.git"
#                APP_VER="$( GIT_DIR="${GIT_DIR}" git describe --tags \
#                               | awk -F "/" '{print $NF}' \
#                               | sed "s/\([^-]*\)-.*/\1/" )"
                APP_VER="$( jq -r '.version' < "${APP_DIRECTORY}/app/package.json" )"
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "webtorrent desktop")
        _rc=0
        if DPKG_CHECK "webtorrent-desktop" ; then
            APP_VER="$( ${PKG_VER} "webtorrent-desktop" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "whisparr")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            APP_HOME="/opt/Whisparr"
            if [ -d "${APP_DIRECTORY}" ] ; then
                if [ -e "${APP_HOME}/version.id" ]
                then
                    APP_VER="$( cat "${APP_HOME}/version.id" )"
                else
                    APP_VER=""
                fi
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "wine")
        _rc=0
        case "${LSB_VENDOR}" in
          "Debian")
              if DPKG_CHECK "wine" ; then
                  APP_VER="$( ${PKG_VER} "wine" )"
                  [ "${op}" = "install" ]   && _rc=111
              else
                  [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
              fi
              ;;
          "Ubuntu")
              if DPKG_CHECK "wine1.8" ; then
                  APP_VER="$( ${PKG_VER} "wine1.8" )"
                  [ "${op}" = "install" ]   && _rc=111
              elif DPKG_CHECK "wine-stable" ; then
                  APP_VER="$( ${PKG_VER} "wine-stable" )"
                  [ "${op}" = "install" ]   && _rc=111
              else
                  [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
              fi
              ;;
        esac
        ;;

    "wireguard")
         _rc=0
        if [ -x "/usr/local/sbin/boringtun" ] ; then
            APP_VER="$( boringtun --version | awk '{print $2}' )"
            [ "${op}" = "install" ] && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "x2go server")
        _rc=0
        if DPKG_CHECK "x2goserver" ; then
            if [ "${op}" = "uninstall" ] ; then
                [ -e "/etc/apt/sources.list.d/nx-libs-beta.list" ] && _rc=112
            else
                APP_VER="$( ${PKG_VER} "x2goserver" )"
                [ "${op}" = "install" ] && _rc=111
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "x2go server (arctica)")
        _rc=0
        if DPKG_CHECK "x2goserver" ; then
            if [ -e "/etc/apt/sources.list.d/nx-libs-beta.list" ] ; then
                APP_VER="$( ${PKG_VER} "x2goserver" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "xtreme download manager")
        _rc=0
        if [ -e "${APP_DIRECTORY}/xdm-rsb.version" ] ; then
            APP_VER="$( cat "${APP_DIRECTORY}/xdm-rsb.version" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "xfce desktop")
        _rc=0
        case "${LSB_VENDOR}" in
          "Debian")
              _pkg="xfce4"
              ;;
          "Ubuntu")
              _pkg="xubuntu-desktop"
              ;;
        esac
        if DPKG_CHECK "${_pkg}" ; then
            APP_VER="$( ${PKG_VER} "${_pkg}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "yandex browser")
        _rc=0
        if DPKG_CHECK "yandex-browser-beta" ; then
            APP_VER="$( ${PKG_VER} "yandex-browser-beta" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "youtube-dl")
        _rc=0
        if [ -x "/usr/local/bin/youtube-dl" ] ; then
            APP_VER="$( python2 /usr/local/bin/youtube-dl --version )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "yt-dlp")
        _rc=0
        if which yt-dlp > /dev/null 2>&1 ; then
            APP_VER="$( yt-dlp --version )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    *)
        LOG="CHECK_APP (): No check procedure defined for: '${o_app}'"
        _rc=1
        ;;

    esac

    JOB_NAME="Application installation status"
    JOB_START "Application installation status"
    [ ${_rc} -eq 111 ] && LOG="CHECK_APP(): '${o_app}' is already installed"
    [ ${_rc} -eq 112 ] && LOG="CHECK_APP(): '${o_app}' is not installed"
    [ ${_rc} -eq 1 ]   && LOG="CHECK_APP(): '${o_app}': Critical error!"
    [ ${_rc} -eq 0 ]   && LOG="CHECK_APP(): '${o_app}': OK to ${op}"
    LOG_RC ${_rc}
    JOB_END

#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


# Check if it safe to install/uninstall application.
# Application name is passed via $1 or determined by $APP content.
# Returns:  0     - if it is safe to install/uninstall application.
#           1     - if required parameter(s) missing.
#           111   - if app is installed and installation requested.
#           112   - if app is not installed and uninstallation was requested.
#           113   - Application is NOT supported on current OS.
#           114   - Another installation procedure is in progress.
#           115   - System uptime is too low ( < 10 minutes ).
#           116   - Resources shortage (f.e. low VPS total memory, not enough free disk space, etc).
#           117   - Application conflicts with one (or more) already installed application(s).
#
function CHECK_APP_YUM () {
#    [ "${DEBUG}" = "yes" ] && ENTRY

    local app
    local o_app
    local script_name
    local op
    local op_RC
    local _pkg
    local _rc

    script_name="$( basename "${0}" )"
    LOG=""
    _rc=0

    if [ -n "${2}" ] ; then
        op="${2}"
    else
        case "${script_name}" in
        install_*)
            op="install"
            ;;
        uninstall_*)
            op="uninstall"
            ;;
        upgrade_*)
            op="upgrade"
            ;;
        __*)
            op="maintenance"
            ;;
        *)
            ERROR "CHECK_APP(): Invalid script name - only install_*.sh/uninstall_*.sh/upgrade_*.sh/__*.sh names allowed!"
            _rc=1
            return ${_rc}
            ;;
        esac
    fi

    o_app="${1}"
    [ -z "${o_app}" ] && o_app="${APP}"
    if [ -z "${o_app}" ] ; then
        ERROR "CHECK_APP_YUM(): Argument(s) missing"
        _rc=1
        return ${_rc}
    fi

#    app="$( echo "${o_app}" | tr '[:upper:]' '[:lower:]' )"
    app="${o_app,,}"

    case "${app}" in

    # Check rpm package status
     "docker" \
     |"filezilla" \
     |"firefox" \
     |"nicotine" \
     |"nomachine" \
     |"pidgin" \
     |"rtorrent" \
     |"softethervpn" \
     |"thunderbird" \
     |"uget" \
     |"vlc" \
     |"waterfox" \
     |"xrdp" \
     )
        _rc=0
        _pkg="${app}"
        [ -n "${SYS_PKG_NAME}" ] && _pkg="${SYS_PKG_NAME}"
        if ${PKG_CHECK} "${_pkg}" ; then
            APP_VER="$( ${PKG_VER} "${_pkg}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    # Check ${APP_DIRECTORY} existence
     "deluge (latest)" \
     |"deluge 1.3.5" \
     |"deluge 1.3.6" \
     |"deluge 1.3.7" \
     |"deluge 1.3.8" \
     |"deluge 1.3.9" \
     |"deluge 1.3.10" \
     |"deluge 1.3.11" \
     |"deluge 1.3.12" \
     |"deluge 1.3.13" \
     |"deluge 1.3.14" \
     |"plexpy" \
     |"rapidleech" \
     |"rsb dashboard" \
     |"softethervpn" \
     |"vinaget" \
     )
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            case "${app}" in
              deluge*)
                  if [ ! -z "${DELUGE_VER}" ] ; then
                      APP_VER="${DELUGE_VER}"
                      if [ -d "${APP_DIRECTORY}" ] ; then
                          [ "${op}" = "install" ]   && _rc=111
                      else
                          [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                      fi
                  else
                      if [ "${app}" = "deluge (latest)" ] ;then
                          if ${PKG_CHECK} "deluge-daemon" ; then
                              APP_VER="$( ${PKG_VER} "deluge-daemon" )"
                              if [ -d "${APP_DIRECTORY}" ] ; then
                                  [ "${op}" = "install" ]   && _rc=111
                              else
                                  [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                              fi
                          fi
                      fi
                  fi
                  ;;
              *)
                  if [ -d "${APP_DIRECTORY}" ] ; then
                      [ "${op}" = "install" ]   && _rc=111
                  else
                      [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                  fi
                  ;;
            esac
        fi
        ;;

    # Check "non-standard" apps :)
    "7zip")
        _rc=0
        if RPM_CHECK "p7zip" ; then
            APP_VER="$( ${PKG_VER} "p7zip" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "aria2")
        _rc=0
        if RPM_CHECK "aria2" ; then
            APP_VER="$( ${PKG_VER} "aria2" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "calibre")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                APP_VER="$( calibre --version | awk '{print $NF}' | tr -d ')' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "deluge 2")
        _rc=0
        if PIP3_CHECK "deluge" ; then
            APP_VER="$( PIP3_VER "deluge" )"
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "deluge - dropbox upload")
        _rc=0
        ;;

    "deluge - google drive upload")
        _rc=0
        ;;

    "deluge - streaming plugin")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [    -d "${APP_DIRECTORY}" \
                 -a -e "${APP_DIRECTORY}/enable_streaming_plugin.py" \
                 -a -e "${APP_DIRECTORY}/disable_streaming_plugin.py" ]
            then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "dropbox cli")
        _rc=0
        if [ -e "${APP_DIRECTORY}/dropbox_uploader.sh" ] ; then
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "flexget")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                which flexget > /dev/null 2>&1 \
                    && APP_VER="$( flexget --version 2> /dev/null | head -1 )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "google chrome")
        _rc=0
        RPM_CHECK "google-chrome-stable"
        if [ $? -eq 0 ] ; then
            APP_VER="$( ${PKG_VER} "google-chrome-stable" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "google drive cli")
        _rc=0
        if [ -e "${APP_DIRECTORY}/gdrive" ] ; then
            APP_VER="$( "${APP_DIRECTORY}/gdrive" version \
                            | grep -m 1 -e '^gdrive:' \
                            | awk '{print $2}' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "handbrake")
        _rc=0
        if [ "${LSB_RELEASE}" = "6" ] ; then
            _pkg="handbrake-gui"
        elif [ "${LSB_RELEASE}" = "7" ] ; then
            _pkg="HandBrake-gui"
        fi
        RPM_CHECK "${_pkg}"
        if [ $? -eq 0 ] ; then
            APP_VER="$( ${PKG_VER} "${_pkg}" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "icecat")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                APP_VER="$( grep -m 1 -e '^Milestone=.*$' "${APP_DIRECTORY}/platform.ini" \
                              | sed -e 's/^Milestone=\(.*\)$/\1/' )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "icewm desktop")
        _rc=0
        if RPM_CHECK "icewm" ; then
            APP_VER="$( ${PKG_VER} "icewm" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "l2tp-ipsec vpn (racoon)")
        _rc=0
        if [ ! -c "/dev/ppp" ] ; then
            ERROR "Device '/dev/ppp' not found!"
            _rc=1
        else
            if RPM_CHECK "racoon" ; then
                APP_VER="$( ${PKG_VER} "racoon" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "l2tp-ipsec vpn (swan)")
        _rc=0
        if [ ! -c "/dev/ppp" ] ; then
            ERROR "Device '/dev/ppp' not found!"
            _rc=1
        else
            if RPM_CHECK "libreswan" ; then
                APP_VER="$( ${PKG_VER} "libreswan" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "mate desktop")
        _rc=0
        if RPM_CHECK "mate-utils-common" ; then
            APP_VER="$( ${PKG_VER} "mate-utils-common" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "openbox desktop")
        _rc=0
        if RPM_CHECK "openbox" ; then
            APP_VER="$( ${PKG_VER} "openbox" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "openvpn 2.4")
        _rc=0
        if RPM_CHECK "openvpn" ; then
            if rpm -q --qf "%{PROVIDEVERSION}" openvpn \
                   | grep -m 1 -q -e '^2\.4\..*' > /dev/null 2>&1
            then
                APP_VER="$( ${PKG_VER} "openvpn" )"
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "opera")
        _rc=0
        if RPM_CHECK "opera-stable" ; then
            APP_VER="$( ${PKG_VER} "opera-stable" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "os update")
        APP_VER="$( lsb_release -sd )"
        _rc=0
        ;;

    "plex")
        _rc=0
        if RPM_CHECK "plexmediaserver" ; then
            APP_VER="$( rpm -q --qf "%{VERSION}" "plexmediaserver" )"
            case "${op}" in
              "install")
                  _rc=111
                  ;;
              "upgrade")
                  _check_mem || _rc=116
                  ;;
            esac
        else
            case "${op}" in
              "install")
                  _check_mem || _rc=116
                  ;;
              "uninstall"|"upgrade")
                  _rc=112
                  ;;
            esac
        fi
        ;;

    "plex webtools")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
            _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                case "${op}" in
                  "install")
                      _rc=111
                      ;;
                  "upgrade")
                      _check_mem || _rc=116
                      ;;
                esac
            else
                case "${op}" in
                  "install")
                      _check_mem || _rc=116
                      ;;
                  "uninstall"|"upgrade")
                      _rc=112
                      ;;
                esac
            fi
        fi
        ;;

    "pure ftpd")
        _rc=0
        if RPM_CHECK "pure-ftpd" ; then
            APP_VER="$( ${PKG_VER} "pure-ftpd" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "rebuild db")
        _rc=0
        ;;

    "resilio sync")
        _rc=0
        if RPM_CHECK "resilio-sync" ; then
            APP_VER="$( ${PKG_VER} "resilio-sync" )"
            if [ -z "${APP_DIRECTORY}" ] ; then
                LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
               _rc=1
            else
                if [ -d "${APP_DIRECTORY}" ] ; then
                    [ "${op}" = "install" ]   && _rc=111
                else
                    [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
                fi
            fi
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "rtorrent")
        _rc=0
        if RPM_CHECK "rtorrent" ; then
            _rc=99
        elif ! grep -q -e \
              '^ExecStart=.*\s*/usr/bin/rtorrent' "/etc/systemd/system/rtorrent@.service" > /dev/null 2>&1
        then
            _rc=99
        elif [ -e "${USERHOME}/.rtorrent.rc" ] ; then
            _rc=99
        fi
        if [ ${_rc} -eq 0 ] ; then
            APP_VER="$( ${PKG_VER} "rtorrent" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            if [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] ; then
                _rc=112
            elif [ "${op}" = "install" ] ; then
                _rc=0
            fi
        fi
        ;;

    "rtorrent 0.9.8")
        _rc=0
        if [ ! -x "/usr/local/rt098/bin/rtorrent" ] ; then
            _rc=99
        elif ! grep -q -e \
              '^ExecStart=.*\s*/usr/local/rt098/bin/rtorrent' \
                   "/etc/systemd/system/rtorrent@.service" > /dev/null 2>&1
        then
            _rc=99
        elif [ ! -e "${USERHOME}/.rtorrent.rc" ] ; then
            _rc=99
        fi
        if [ ${_rc} -eq 0 ] ; then
            APP_VER="0.9.8"
            [ "${op}" = "install" ]   && _rc=111
        else
            if [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] ; then
                _rc=112
            elif [ "${op}" = "install" ] ; then
                _rc=0
            fi
        fi
        ;;

    "rutorrent (latest)" \
     |"rutorrent (rtorrent 0.9.2)" \
     |"rutorrent (rtorrent 0.9.3)" \
     |"rutorrent (rtorrent 0.9.4)" \
     |"rutorrent (rtorrent 0.9.6)" \
     |"rutorrent (rtorrent 0.9.8)" \
     |"rutorrent ui" \
     |"RuTorrent" )
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}/conf/users/${USERNAME}" ] ; then
                if [ "${op}" = "install" ] ; then
                    if [ "${app}" = "rutorrent (latest)" ] ; then
                        if which rtorrent > /dev/null 2>&1 ; then
                            _rc=111
                        fi
                    else
                        _rc=111
                    fi
                fi
                GIT_DIR="${APP_DIRECTORY}/.git"
                if [ -d "${GIT_DIR}" ] ; then
                    APP_VER="$( GIT_DIR="${GIT_DIR}" git describe --tags \
                                   | awk -F "/" '{print $NF}' \
                                   | sed "s/\(.*\)-.*/\1/" \
                                   | tr "-" "_" )"
                else
                    APP_VER="$( grep -m 1 -E '\s+version:' "${APP_DIRECTORY}/js/webui.js" \
                                 | awk '{print $NF}' \
                                 | tr -d '",' )"
                fi
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "rutorrent - dropbox upload")
        _rc=0
        ;;

    "rutorrent - filebot")
        _rc=0
        ;;

    "rutorrent - google drive upload")
        _rc=0
        ;;

    "rutorrent - mediastream plugin")
        _rc=0
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ]   && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "seedboxcp")
        _rc=0
        DEBUG "${app}: APP_DIRECTORY: [${APP_DIRECTORY}]"
        if [ -z "${APP_DIRECTORY}" ] ; then
            LOG="CHECK_APP(): \$APP_DIRECTORY is not defined!"
           _rc=1
        else
            if [ -d "${APP_DIRECTORY}" ] ; then
                [ "${op}" = "install" ] && _rc=111
            else
                [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
            fi
        fi
        ;;

    "subliminal")
        _rc=0
        if PIP3_CHECK "subliminal" ; then
            APP_VER="$( pip3 show subliminal 2>/dev/null | grep -m 1 -E "^Version:" | awk '{print $2}' )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "thinlinc")
        _rc=0
        if RPM_CHECK "thinlinc-webaccess" ; then
            APP_VER="$( ${PKG_VER} "thinlinc-webaccess" )"
            [ "${op}" = "install" ]   && _rc=111
        elif RPM_CHECK "thinlinc-server" ; then
            APP_VER="$( ${PKG_VER} "thinlinc-server" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "update rsb custom scripts")
        _rc=0
        ;;

    "update rsb custom scripts - test")
        _rc=0
        ;;

    "utorrent 2.2.1 (wine)")
        _rc=0
        if [ -e "${APP_DIRECTORY}/utorrent_2.2.1_build_25302.exe" ] ; then
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "vivaldi")
        _rc=0
        if RPM_CHECK "vivaldi-stable" ; then
            APP_VER="$( ${PKG_VER} "vivaldi-stable" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

     "vnc")
        _rc=0
        if RPM_CHECK "tigervnc-server" ; then
            APP_VER="$( ${PKG_VER} "tigervnc-server" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "wine")
        _rc=0
        if RPM_CHECK "wine" ; then
            APP_VER="$( ${PKG_VER} "wine" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "x2go server")
        _rc=0
        if RPM_CHECK "x2goserver" ; then
            APP_VER="$( ${PKG_VER} "x2goserver" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "xfce desktop")
        _rc=0
        if RPM_CHECK "libxfce4ui" ; then
            APP_VER="$( ${PKG_VER} "libxfce4ui" )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "youtube-dl")
        _rc=0
        if which youtube-dl > /dev/null 2>&1 ; then
            APP_VER="$( youtube-dl --version )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    "yt-dlp")
        _rc=0
        if which yt-dlp > /dev/null 2>&1 ; then
            APP_VER="$( yt-dlp --version )"
            [ "${op}" = "install" ]   && _rc=111
        else
            [ "${op}" = "uninstall" -o "${op}" = "upgrade" ] && _rc=112
        fi
        ;;

    *)
        LOG="CHECK_APP (): No check procedure defined for: '${o_app}'"
        _rc=1
        ;;
    esac

    JOB_NAME="Application installation status"
    JOB_START
    [ ${_rc} -eq 111 ] && LOG="CHECK_APP(): '${o_app}' is already installed"
    [ ${_rc} -eq 112 ] && LOG="CHECK_APP(): '${o_app}' is not installed"
    [ ${_rc} -eq 1 ]   && LOG="CHECK_APP(): '${o_app}': Critical error!"
    [ ${_rc} -eq 0 ]   && LOG="CHECK_APP(): '${o_app}': OK to ${op}"
    LOG_RC ${_rc}

#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}


function CHECK_APP () {
    local _rc=1
    local _conflicts
    local _extra_vars
    local _tmp_file
    local _s
    local _status
    local oIFS

#    [ "${DEBUG}" = "yes" ] && ENTRY

    if [ ! -e "${APP_DB}" -o ! -s "${APP_DB}" ] ; then
        # Initialize APP DB
        _init_db
    fi

    __update_json_status "running" "${operation2}: ${APP}"

    # Check for CONFLICTS
    if [ ! -z "${APP}" ] ; then
        if [   "${operation2}" = "Installation" \
            -o "${operation2}" = "Upgrade" ]
        then
            JOB_START "Checking for conflicts"
            _conflicts="$( ${SQL_CMD} "${APP_DB}" "SELECT conflicts FROM application WHERE name = '${APP}';" )"
            DEBUG "_conflicts: [${_conflicts}]"
            oIFS="${IFS}"
            IFS='|'
            for _s in ${_conflicts} ; do
                _status="$( ${SQL_CMD} "${APP_DB}" "SELECT status FROM application WHERE name = '${_s}';" )"
                if [ "${_status}" = "1" ] ; then
                    ERROR          "'${APP}' conflicts with '${_s}' which is installed!"
                    INSTALLAPP_MSG "'${APP}' conflicts with '${_s}' which is installed!"
                    _rc=117
                    JOB_END "Checking for conflicts. Rc: [${_rc}]"
                    return ${_rc}
                fi
            done
            IFS="${oIFS}"
            JOB_END "Checking for conflicts"
        fi
    fi

    # Set default values for the variables that might be overwritten by `extra_vars` from ${APP_DB}
    MEMORY_K_MIN=0
    DISK_K_MIN=0

    # Include Extra variables for this application
    if [ ! -z "${APP}" ] ; then
        _extra_vars="$( ${SQL_CMD} "${APP_DB}" \
                            "SELECT extra_vars FROM application WHERE name = '${APP}';" )"
        if [ ! -z "${_extra_vars}" ] ; then
            JOB_START "Processing Extra variables"
            DEBUG "_extra_vars: [${_extra_vars}]"
            eval "${_extra_vars}"
            JOB_END "Processing Extra variables"
        fi
    fi

    case "${LSB_VENDOR}" in
      "CentOS"|"CentOSStream")
          CHECK_APP_YUM "${1}" "${2}"
          _rc=$?
          ;;
      "Debian"|"Devuan"|"Ubuntu")
          CHECK_APP_DEB "${1}" "${2}"
          _rc=$?
          ;;
    esac
    if [ ${_rc} -ne 0 ] ; then
        [ "${DEBUG}" = "yes" ] && RETURN
        return ${_rc}
    fi

    # We do not want to run the rest of the operations
    #     for "Uninstall" script
    if [ "${operation2}" = "Uninstallation" ] ; then
        [ "${DEBUG}" = "yes" ] && RETURN
        return ${_rc}
    fi

    ${PKG_UPDATE} || {
        ERROR          "Error(s) updating PKG cache for '${LSB_VER}' !"
        INSTALLAPP_MSG "Error(s) updating PKG cache for '${LSB_VER}' !"
        L_LOG
        main_rc=1
        SCRIPTEXIT
    }

    # Do not upgrade some packages
    # --- numix-gtk-theme numix-icon-theme numix-icon-theme-circle

    if [ "${DIST_UPGRADE_ENABLED}" = "yes" ] ; then
        ${DIST_UPGRADE} || {
            ERROR          "Error(s) upgrading distribution '${LSB_VER}' !"
            INSTALLAPP_MSG "Error(s) upgrading distribution '${LSB_VER}' !"
            L_LOG
            main_rc=1
            SCRIPTEXIT
        }
    fi

    # Install '?locate' package
    local locate_pkg=""

    case "${LSB_VENDOR}" in
        Debian|Ubuntu|Devuan)
            if apt-cache show plocate >/dev/null 2>&1 ; then
                locate_pkg="plocate"
            elif apt-cache show mlocate >/dev/null 2>&1 ; then
                locate_pkg="mlocate"
            elif apt-cache show slocate >/dev/null 2>&1 ; then
                locate_pkg="slocate"
            else
                locate_pkg=""
            fi
            ;;
        CentOS|CentOSStream|Fedora|RedHatEnterpriseServer)
            if (dnf --quiet list plocate >/dev/null 2>&1) || (yum --quiet list plocate >/dev/null 2>&1) ; then
                locate_pkg="plocate"
            elif (dnf --quiet list mlocate >/dev/null 2>&1) || (yum --quiet list mlocate >/dev/null 2>&1) ; then
                locate_pkg="mlocate"
            else
                locate_pkg=""
            fi
            ;;
        *)
            locate_pkg=""
            ;;
    esac

    if [[ -n "${locate_pkg}" ]] ; then
        ${PKG_CHECK} "${locate_pkg}"
        if [ $? -ne 0 ] ; then
            ${PKG_INSTALL} "${locate_pkg}" && RUN_LOG "updatedb"
        fi
    fi

    # Regenerate English locale(s)
    case "${LSB_VENDOR}" in
      "CentOS"|"CentOSStream")
          ;;
      "Debian"|"Ubuntu")
          JOB_START "Regenerating English locale"
          __run_log "timeout -k 9 300 locale-gen en"
          _rc=$?
          if [ "${_rc}" = "124" ] ; then
              WARN "Timeout reached - regeneration ALL locales. Keep patience..."
              __run_log "locale-gen"
          else
              __run_log "locale-gen en_US"
          fi
          JOB_END "Regenerating English locale"
          _rc=0
          ;;
    esac

    # Install required packages
    JOB_START "Installing required packages"
    ${PKG_INSTALL} ${REQ_PACKAGES} || {
        ERROR          "Error(s) installing essential packages for '${LSB_VER}' !"
        INSTALLAPP_MSG "Error(s) installing essential packages for '${LSB_VER}' !"
        L_LOG
        main_rc=1
        SCRIPTEXIT
    }
    JOB_END "Installing required packages"

#    # Check if Apache2 is needed
#    if [ -d "${APACHE_CONF_DIR}" ] || [ "${USE_APACHE2}" = "yes" ] ; then
#        if [ "${LSB_VENDOR}" = "Debian" -o "${LSB_VENDOR}" = "Ubuntu" ] ; then
#            _prep_apache2_deb
#        elif [ "${LSB_VENDOR}" = "CentOS" ] ; then
#            _prep_apache2_centos
#        elif [ "${LSB_VENDOR}" = "CentOSStream" ] ; then
#            _prep_apache2_centos
#        fi
#        # Modify user group membership
#        RUN_LOG "usermod -a -G ${APACHE_GROUP} ${USERNAME}"
#
#        # Allow apache group to write to user's Download directory
#        RUN_LOG "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX /etc/skel/Downloads"
#        RUN_LOG "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX /home/user/Downloads"
#        RUN_LOG "setfacl -Rm g:${APACHE_USER}:rwX,d:g:${APACHE_GROUP}:rwX ${USERHOME}/Downloads"
#    fi
#    if [ -e "${APACHE_CONF_DIR}/ports.conf" ] ; then
#        if grep -q -e '^\s*Listen 80\s*$' "${APACHE_CONF_DIR}/ports.conf" 2> /dev/null ; then
#            INFO "Disable 'Listen 80' directive"
#            RUN_LOG "sed -i -e 's|^\s*\(Listen 80\)\s*$|#\1|' /etc/apache2/ports.conf"
#            SVC_STATUS "apache2" \
#                && SVC_RESTART "apache2"
#        fi
#    fi

    # Initialize APP DB
    #    (if not called from `install-app` - we already did it I hope :) )
    if [ "${INSTALL_MODE}" != "interactive" ] ; then
        _init_db
    fi

#    [ "${DEBUG}" = "yes" ] && RETURN
    return ${_rc}
}
