#!/bin/bash

#
# Name:         install-app
# Version:      1.3.100
#
# (c) 2016-2023, Rapidseedbox, alexey@rapidseedbox.com
#

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

shopt -u checkwinsize

# Fix "bad" terminals
case "${TERM}" in
  xterm* )
      export TERM="linux"
      ;;
esac


SC_NAME="$( basename "${0}" )"
_VER="$( grep -m 1 -E '^#\s*Version:\s+' "${0}" | awk '{print $NF}' )"
_PID="$$"

PASSWORDS_STORE_DIR="/var/local/passwd"


# Quick checks before we allow bad things to happen
if [ -z "${BASH_VERSINFO}" ]; then
    >&2 echo "ERROR: You must execute this script with BASH"
    exit 1
fi

_uid="$( id -u )"
if [ ${_uid} -ne 0 ] ; then
    >&2 echo "Sorry."
    >&2 echo "root credentials required to run this script !"
    exit 1
fi

if ! which lsb_release > /dev/null 2>&1 ; then
    >&2 echo "'lsb_release': command not found!"
    >&2 echo "Can not continue..."
    exit 1
fi

[ -d "/var/log/whmcs_client_installers" ] \
    || mkdir -p "/var/log/whmcs_client_installers" > /dev/null 2>&1


_usage() {
    cat <<-EOF
	Usage:
	    ${SC_NAME} [-h] [-b] [-l] [-n] [-t] [-s "search string"] [-d] [-u username] [-p user_password] [-z altURL] [-a app1:op1[,app2:op2 ...]] [ [--] [username] [password] ]

	        -h      - Display this message
	        -b      - Black & White mode
	        -l      - List available applications
	        -n      - Do not try to update "${SC_NAME}" to the latest version
	        -t      - Use test version. You have been warned :)
	        -s      - Search for specific application
	        -d      - Debug mode
	        -u      - Specify username
	        -p      - Specify password
	        -a      - Install / Uninstall / Upgrade application(s) in batch mode
	                  Examples:
	                     -a xrdp:install
	                     -a xrdp:install,flexget:uninstall
	                     -a dist_upgrade:upgrade
	                     ........
	        -z      - alt. URL
	EOF
}


if [ "${1}" = "-h" ] || [ "${1}" = "--help" ] ; then
    _usage
    exit 0
fi

IP="$( ip -o route get to 8.8.8.8 | sed -nre 's/.*\s+src\s+([0-9.]+).*/\1/p' )"

if    [ -e "/etc/cron.d/easy-ssl-v2" ] \
   && [ -e "/etc/apache2/sites-enabled/rsbssl_easyV2_my.seedbox.vip.conf" ]
then
    HTTPS_HOST="$( awk '$1 == "ServerName" {print $NF}' \
                     "/etc/apache2/sites-enabled/rsbssl_easyV2_my.seedbox.vip.conf" \
                   | head -1 )"
elif  [ -e "/etc/cron.d/easy-ssl" ] \
   && [ -e "/etc/apache2/sites-enabled/rsbssl_easy_a.seedbox.vip.conf" ]
then
    HTTPS_HOST="$( awk '$1 == "ServerName" {print $NF}' \
                     "/etc/apache2/sites-enabled/rsbssl_easy_a.seedbox.vip.conf" \
                   | head -1 )"
else
    HTTPS_HOST="${IP}"
fi

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

# Collect some usage statistics :)
RSB_STATS_DIR="/var/local/stats"
[ ! -d "${RSB_STATS_DIR}" ] && mkdir -p "${RSB_STATS_DIR}"
if [ -e "${RSB_STATS_DIR}/${SC_NAME}.run.cnt" ] ; then
    run_cnt="$( cat "${RSB_STATS_DIR}/${SC_NAME}.run.cnt" )"
    run_cnt="$((run_cnt + 1))"
else
    run_cnt="1"
fi
echo "${run_cnt}" > "${RSB_STATS_DIR}/${SC_NAME}.run.cnt"

# 'Fix' /etc/resolv.conf
echo -e "nameserver 8.8.8.8\nnameserver 8.8.4.4" > "/etc/resolv.conf"
#

# Installers configuration local storage
INSTALLERS_CONF_DIR="/var/local/whmcs_client_installers"
[ ! -d "${INSTALLERS_CONF_DIR}" ] \
    && mkdir -p "${INSTALLERS_CONF_DIR}"


export INSTALL_MODE="interactive"

export DEBUG="no"
export TEST="no"
export ALT_URL="no"
DIST_UPGRADE_ENABLED="yes"
REBOOT_RECOMMENDED="no"
REBOOT_CONFIRMED="no"
CLEAN_TMP="yes"

main_rc=0

LOCK_FILE="/var/lock/whmcs_client_installers.lock"
STATUS_FILE="/var/lock/whmcs_client_installers.status"

if ! flock -w 1 "${LOCK_FILE}" date > /dev/null 2>&1 ; then
    >&2 echo "Another application (un)installation is in progress!!!"
    if [ -e "${STATUS_FILE}" ] ; then
        script_name="$( awk -F'|' '{print $1}' "${STATUS_FILE}" )"
        script_pid="$(  awk -F'|' '{print $2}' "${STATUS_FILE}" )"
        start_time="$(  awk -F'|' '{print $3}' "${STATUS_FILE}" )"
        start_time="$( date -d "@${start_time}" -u )"
        >&2 echo "${script_name} [PID: ${script_pid}]: Running since ${start_time}"
    fi
    exit 100
fi

export _SCRIPT_ARGS=$@

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:]]*$//' \
                  )"
# 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:]' )"
[ "${LSB_VENDOR}" = "CentOS" -o "${LSB_VENDOR}" = "Debian" ] \
    && LSB_RELEASE="$( echo "${LSB_RELEASE}" | awk -F'.' '{print $1}' )"
LSB_VER="${LSB_VENDOR}${LSB_RELEASE}"

# Check for unsopported distributions/versions
case "${LSB_VENDOR}" in
  "CentOS")
      case "${LSB_RELEASE}" in
        "6")
            echo -e "Sorry.\n${LSB_VENDOR} ${LSB_RELEASE} is not supported."
            exit
            ;;
      esac
      ;;
esac

(
flock -n 9

echo "${0} ${*}|$$|$( date -u +'%s' )" > "${STATUS_FILE}"

# Define the dialog exit status codes
DIALOG_OK=0
DIALOG_CANCEL=1
DIALOG_HELP=2
DIALOG_EXTRA=3
DIALOG_ITEM_HELP=4
DIALOG_ESC=255
DIALOG_OPTS="--ascii-lines --no-collapse --colors"

RSB_URL="https://installers.rapidseedbox.com/scripts/whmcs_client_installers"

LOGFILE="/var/log/${SC_NAME}.log"



# Default settings
USERNAME="user"
USERPASSWORD="ChangeMe"

# Parse command line
OPTIND=1
PARSE_ERR=0
_title_pfx=""
export INSTALLAPP_UPDATE_CHECK="yes"
SEARCH=""
NO_COLORS="no"
BASH_OPTS=""
APP_LIST=""
while getopts ":s:u:p:z:a:dhbltnz" params; do
    case "${params}" in
      s)
          SEARCH="${OPTARG}"
          if [[ ${OPTARG} = -* ]] ; then
            >&2 echo "Option -${params} requires an argument." >&2
            PARSE_ERR=1
            ((OPTIND--))
            continue
          fi
          ;;
      u)
          USERNAME="${OPTARG}"
          if [[ ${OPTARG} = -* ]] ; then
            >&2 echo "Option -${params} requires an argument." >&2
            PARSE_ERR=1
            ((OPTIND--))
            continue
          fi
          ;;
      p)
          USERPASSWORD="${OPTARG}"
          if [[ ${OPTARG} = -* ]] ; then
            >&2 echo "Option -${params} requires an argument." >&2
            PARSE_ERR=1
            ((OPTIND--))
            continue
          fi
          ;;
      z)
          RSB_URL="${OPTARG}"
          ALT_URL="yes"
          DIST_UPGRADE_ENABLED="no"
          if [[ ${OPTARG} = -* ]] ; then
            >&2 echo "Option -${params} requires an argument." >&2
            PARSE_ERR=1
            ((OPTIND--))
            continue
          fi
          ;;
      a)
          APP_LIST="${OPTARG}"
          if [[ ${OPTARG} = -* ]] ; then
            >&2 echo "Option -${params} requires an argument." >&2
            PARSE_ERR=1
            ((OPTIND--))
            continue
          fi
          ;;
      d)
          DEBUG="yes"
          BASH_OPTS="-xv"
          continue
          ;;
      h)
          _usage ; exit 0
          ;;
      b)
          NO_COLORS="yes"
          continue
          ;;
      l)
          LIST_ONLY="yes"
          continue
          ;;
      n)
          export INSTALLAPP_UPDATE_CHECK="no"
          continue
          ;;
      t)
          DIST_UPGRADE_ENABLED="no"
          CLEAN_TMP="no"
          TEST="yes"
          ls -A /var/tmp/${SC_NAME}.* > /dev/null 2>&1 \
              && rm -rf /var/tmp/${SC_NAME}.*
          continue
          ;;
      \?) 
          >&2 echo "Invalid option: -${OPTARG}" >&2
          PARSE_ERR=1
          ;;
      :)
          >&2 echo "Option -${OPTARG} requires an argument." >&2
          PARSE_ERR=1
          ;;
    esac
done
if [ ${PARSE_ERR} -ne 0 ] ; then
    _usage ; exit 2
fi
shift "$(( OPTIND - 1 ))"

if [ -n "${1}" ] ; then
    [ -z "${USERNAME}" ] && USERNAME="${1}"
    if [ -n "${2}" ] ; then
        [ -z "${USERPASSWORD}" ] && USERPASSWORD="${2}"
    fi
fi


# ------------------------------------------------------- #
# -- Turn on DEBUG mode if requested                   -- #
# ------------------------------------------------------- #
if [ "${DEBUG}" = "yes" ] ; then
    if [ -z "${DEBUG_LOGFILE}" ] ; then
        DEBUG_LOGFILE="/var/log/whmcs_client_installers/dbg-${SC_NAME}.log"
    fi
    export DEBUG_LOGFILE
    echo -e "\n\n=============================================================================================" \
        >> "${DEBUG_LOGFILE}"
    echo -e "$( date -u ): Starting ..." >> "${DEBUG_LOGFILE}"
    echo -e "$(     date -u ): Command:  [${SC_NAME} $( echo "$( for _a in "$@" ; do printf %s "'${_a//\'/\'\\\'\'}' " ; done | sed -re 's/\s*$//'  )" )]" >> "${DEBUG_LOGFILE}"
    echo -e "=============================================================================================\n" \
        >> "${DEBUG_LOGFILE}"
    exec {BASH_XTRACEFD}>>"${DEBUG_LOGFILE}"
    set -x
fi

PASSWORDS_STORE="${PASSWORDS_STORE_DIR}/${USERNAME}"

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


_title_pfx="${USERNAME}"
if [ "${TEST}" = "yes" ] ; then
    [ -n "${_title_pfx}" ] && _title_pfx="${_title_pfx},"
    _title_pfx="${_title_pfx}TEST"
    RSB_URL="${RSB_URL}_test"
elif [ "${ALT_URL}" = "yes" ] ; then
        [ -n "${_title_pfx}" ] && _title_pfx="${_title_pfx},"
        _title_pfx="${_title_pfx}altURL"
fi
if [ "${DEBUG}" = "yes" ] ; then
    [ -n "${_title_pfx}" ] && _title_pfx="${_title_pfx},"
    _title_pfx="${_title_pfx}DEBUG"
fi
[ -n "${_title_pfx}" ] && _title_pfx="[${_title_pfx}] "

export RSB_URL
export SUPPORT="${RSB_URL}/support"

APP_DB_DIR="/var/db/whmcs_client_installers"
[ ! -d "${APP_DB_DIR}" ] && mkdir -p "${APP_DB_DIR}"
APP_DB="${APP_DB_DIR}/app.db"
if [ "${USERNAME}" != "user" ] ; then
    APP_DB="${APP_DB_DIR}/app-${USERNAME}.db"
fi

USERHOME="$( awk -F: -v u="${USERNAME}" '$1 == u {print $6}' /etc/passwd )"

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



if [ "${INSTALLAPP_UPDATE_CHECK}" = "yes" ] ; then
    wget -nc -q --no-check-certificate \
         -O "/var/tmp/${SC_NAME}.${_PID}" \
         --timeout=20 --tries=5 \
         "${RSB_URL}/support/rsb-scripts/common/install-app" >> "${LOGFILE}" 2>&1
    if [ $? -ne 0 ] ; then
         >&2 echo -e "\nError connecting to ${RSB_URL}\nPlease try again later."
         exit 1
    fi
    _rc=0
    _sfx=""
    _NEW_VER="$( grep -m 1 -E '^#\s*Version:\s+' "/var/tmp/${SC_NAME}.${_PID}" \
                     | awk '{print $NF}' )"
    if which diff > /dev/null 2>&1 ; then
        diff "/var/tmp/${SC_NAME}.${_PID}" "/usr/local/bin/${SC_NAME}" > /dev/null 2>&1
        _rc=$?
        [ ${_rc} -ne 0 -a "${_NEW_VER}" = "${_VER}" ] \
            && _sfx=" (modified)"
    else
        [ "${_NEW_VER}" != "${_VER}" ] \
            && _rc=1
    fi
    if [ ${_rc} -ne 0 ] ; then
        >&2 echo "install-app ${_VER}: updating to ${_NEW_VER}${_sfx} and restarting..."
        mv -f "/var/tmp/${SC_NAME}.${_PID}" "/usr/local/bin/${SC_NAME}"
        [ -e "/var/lock/whmcs_client_installers.lock" ] \
            && rm -f "/var/lock/whmcs_client_installers.lock"
        chmod -f 755 "/usr/local/bin/${SC_NAME}"
        >&2 exec "${0}" -n ${_SCRIPT_ARGS}
        exit 0
    else
        [ -e "/var/tmp/${SC_NAME}.${_PID}" ] \
            && rm -rf "/var/tmp/${SC_NAME}.${_PID}" > /dev/null 2>&1
    fi
fi


# TERM dimensions
max_lines="$( tput lines )"
export max_lines="$(( max_lines - 3 ))"
export max_columns="$( tput cols )"

>&2 echo "install-app ${_VER}: starting..."
if [ "${TEST}" = "yes" -o "${ALT_URL}" = "yes" ] ; then
    >&2 echo -e "USERNAME:\t[${USERNAME}]"
    >&2 echo -e "USERHOME:\t[${USERHOME}]"
    >&2 echo -e "RSB_URL:\t[${RSB_URL}]"
fi
>&2 echo "Please wait a moment :)"

LIB_DIR="/usr/local/lib/rsb"
if [ ! -d "${LIB_DIR}" ] ; then
    >&2 echo "Creating '${LIB_DIR}' directory..."
    if ! mkdir -p "${LIB_DIR}" >> "${LOGFILE}" 2>&1 ; then
        >&2 echo "Error creating '${LIB_DIR}' directory!" >> "${LOGFILE}"
        exit 1
    fi
fi
>&2 echo "Fetching shell common library..."
wget -nc -q --no-check-certificate \
     -O "/var/tmp/functions.sh.${_PID}" \
     --timeout=20 --tries=5 \
     "${RSB_URL}/support/rsb-scripts/lib/functions.sh" >> "${LOGFILE}" 2>&1
if [ $? -ne 0 ] ; then
    >&2 echo "Error downloading '${RSB_URL}/support/functions.sh'" >> "${LOGFILE}"
    exit 1
fi
_rc=1
if which diff > /dev/null 2>&1 ; then
    diff "/var/tmp/functions.sh.${_PID}" "${LIB_DIR}/functions.sh" > /dev/null 2>&1
    _rc=$?
fi
if [ ${_rc} -ne 0 ] ; then
    >&2 echo "functions.sh: updating..."
    mv -f "/var/tmp/functions.sh.${_PID}" "${LIB_DIR}/functions.sh"
    chmod -f 0644 "${LIB_DIR}/functions.sh"
else
    [ -e "/var/tmp/functions.sh.${_PID}" ] \
        && rm -rf "/var/tmp/functions.sh.${_PID}" > /dev/null 2>&1
fi

. "${LIB_DIR}/functions.sh"

export INSTALLAPP_TMP_DIR="${TMP_DIR}"

export USERUID="$( id -u "${USERNAME}" 2> /dev/null )"



#--------------------------------------------------------------------------------
# Functions
#--------------------------------------------------------------------------------

DEBUG () {
    [ "${DEBUG}" = "yes" ] || return
    local msg="${1}"
    local tstamp="$( date )"
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
}


TESTLOG () {
    [ "${TEST}" = "yes" ] || return
    local msg="${1}"
    local tstamp="$( date )"
    echo -e "[${tstamp}] [TEST]\t${msg}" >> "${LOGFILE}"
}


_clean_tmp () {
    if [ "${CLEAN_TMP}" = "yes" ] ; then
        [ -d "${TMP_DIR}" ] && RM_DIRS "${TMP_DIR}"
        ls -A /var/tmp/${SC_NAME}.* > /dev/null 2>&1 \
            && RM_FILES /var/tmp/${SC_NAME}.*
        ls -A /var/tmp/functions.sh.* > /dev/null 2>&1 \
            && RM_FILES /var/tmp/functions.sh.*
    fi
}


_exit () {
    local i
    if [ "${REBOOT_CONFIRMED}" = "yes" ] ; then
        for ((i=0;i<=100;i+=10)) ; do
            cat <<-EOF
		XXX
		${i}
		Restarting the box in $(((100-${i})/10)) seconds
		XXX
		EOF
            sleep 1
        done \
            | dialog ${DIALOG_OPTS} \
                  --backtitle "$( _backtitle "Reboot pending..." )" \
                  --gauge "" \
                  7 50
        clear
        reset
        _clean_tmp
        [ -e "/var/run/reboot-required" ] && RM_FILES "/var/run/reboot-required"
        reboot
    fi

    # Quit !
    if [ ${main_rc} -ne 0 ] ; then
        [ -e "${TMP_DIR}/install-app.msg" ] \
            && >&2 echo -ne "$( cat "${TMP_DIR}/install-app.msg" )\\n"
        >&2 echo -e "Errors encountered.\nPlease see:\n${LOGFILE}\n${_sc_logfile}\nfor details !"
    fi

    _clean_tmp
    exit ${main_rc}
}


_exit_cls () {
    local _reboot_required="no"
    [ -e "/var/run/reboot-required" ] && _reboot_required="yes"
    if [ "${_reboot_required}" = "yes" ] ; then
        dialog ${DIALOG_OPTS} \
           --backtitle "$( _backtitle "Reboot recommended!" )" \
           --yesno "Are you sure you want to reboot the box ?" \
             0 0 \
           && REBOOT_CONFIRMED="yes"
    fi
    clear
    reset
    _exit
}


_rpm_ver () {
    rpm -q --qf "%{PROVIDEVERSION}" "${1}" 2> /dev/null
#    rpm -q --qf "%{VERSION}" "${1}" 2> /dev/null
}

_dpkg_ver () {
    dpkg -s "${1}" 2> /dev/null | grep -m 1 -e '^Version:.*$' | awk '{print $NF}'
}

#
# Purpose - convert passed number of seconds to the string of this format:
#              1 day, 3 hours, 37 minutes, 39 seconds 
str_time_format () {
    local _secs="${1}"
    local n_days
    local n_hours
    local n_minutes
    local n_seconds
    local days
    local hours
    local minutes
    local seconds
    local t_str

    n_seconds="$(( _secs % 60 ))"
    n_minutes="$(( _secs % 3600 / 60 ))"
    n_hours="$((   _secs / 3600 % 24 ))"
    n_days="$((    _secs / 3600 / 24 ))"

    days="days"       ; [ "${n_days}" = "1" ]    && days="day"
    hours="hours"     ; [ "${n_hours}" = "1" ]   && hours="hour"
    minutes="minutes" ; [ "${n_minutes}" = "1" ] && minutes="minute"
    seconds="seconds" ; [ "${n_seconds}" = "1" ] && seconds="second"

    t_str=""
    if [ "${n_days}" != "0" ] ; then
        t_str="${n_days} ${days}"
    fi
    if [ -n "${t_str}" ] ; then
        t_str="${t_str}, ${n_hours} ${hours}"
    else
        if [ "${n_hours}" != "0" ] ; then
            t_str="${n_hours} ${hours}"
        fi
    fi
    if [ -n "${t_str}" ] ; then
        t_str="${t_str}, ${n_minutes} ${minutes}"
    else
        if [ "${n_minutes}" != "0" ] ; then
            t_str="${n_minutes} ${minutes}"
        fi
    fi
    if [ "${n_seconds}" != "0" ] ; then
        if [ -n "${t_str}" ] ; then
            t_str="${t_str}, ${n_seconds} ${seconds}"
        else
            t_str="${n_seconds} ${seconds}"
        fi
    fi

    echo "${t_str}"
}


#
# Echo the backtitle string in form 'Backtitle ... spaces ... [_VER]'
_backtitle () {
    local v_len
    local t_len
    local n_spaces
    local bt

    bt="${_title_pfx}${LSB_VENDOR} ${LSB_RELEASE}: ${1}"

    t_len="$( echo "${bt}" | wc -c )"
    v_len="$( echo "${_VER}" | wc -c )"
    n_spaces="$(( max_columns - t_len - v_len ))"

    printf "%s%${n_spaces}s%s" "${bt}" " " "${_VER}"
}


#
# Echo the menu item string in form 'Item_help ... spaces ... Item_help_extra'
_menu_item_help () {
    local e_len
    local t_len
    local n_spaces
    local item_help="${1}"
    local item_help_extra="${2}"

    t_len="$( echo "${item_help}" | sed -e 's/\\Z.//g' | wc -c )"
    e_len="$( echo "${item_help_extra}" | sed -e 's/\\Z.//g' | wc -c )"
    n_spaces="$(( max_columns - t_len - e_len ))"

    printf "%s%${n_spaces}s%s" "${item_help}" " " "${item_help_extra}"
}


#
# Purpose - display output using msgbox 
#  $1 -> set msgbox height
#  $2 -> set msgbox width
#  $3 -> set msgbox title
#  $4 -> set msgbox backtitle
#
display_output () {
    local h=${1-10}                 # box height default 10
    local w=${2-41}                 # box width default 41
    local t=${3-Output}             # box title 
    local b=${4-Output}             # backtitle
    local ib="${5}"                 # infobox data (man dialog)
    local ib_title
    local ib_message
    local NL

    DEBUG "\n\ndisplay_output:\n\th:\t[${h}]\n\tw:\t[${w}]\n\tt:\t[${t}]\n\tb:\t[${b}]\n\tib:\t[${ib}]\n"
    NL="$( wc -l < "${OUTPUT}" )"
    [ ${NL} -gt 10 ] && h=0 || h="$(( NL + 7 ))"
    if [ -n "${ib}" ] ; then
        ib_title="$(   echo "${ib}" | awk -F '|' '{print $1}' )"
        ib_message="$( echo "${ib}" | awk -F '|' '{print $2}' )"
        dialog ${DIALOG_OPTS} \
               --begin 2 2 \
               --no-lines \
               --no-shadow \
               --title "${ib_title}" \
               --infobox "${ib_message}" 0 0 \
           --and-widget \
               --keep-window \
               --ascii-lines \
               --aspect 14 \
               --backtitle "$( _backtitle "${b}" )" \
               --title "${t}" \
               --msgbox "$( cat "${OUTPUT}" )" 0 0
    else
        dialog ${DIALOG_OPTS} \
               --backtitle "$( _backtitle "${b}" )" \
               --aspect 14 \
               --hline "${hl}" \
               --title "${t}" \
               --msgbox "$( cat "${OUTPUT}" )" "${h}" "${w}"
    fi
}


_rebuild_db () {
    local _rc=1
    local oDIST_UPGRADE_ENABLED="${DIST_UPGRADE_ENABLED}"
    local _sc_name="__rebuild_db.sh"
    local _sc_logdir="/var/log/whmcs_client_installers"
    local _sc_logfile="${_sc_logdir}/${_sc_name}.log"
    local opwd
    local mode

    mode="${1}"

    >&2 echo "Fetching Applications DB..."

    DIST_UPGRADE_ENABLED="no"

    opwd="$( pwd )"
    cd "${TMP_DIR}"
    while true ; do
        WGET "${RSB_URL}/${_sc_name}" || break
        if [ ! -s "${TMP_DIR}/${_sc_name}" ] ; then
            >&2 echo "Error fetching the script: [${RSB_URL}/${_sc_name}]"
            break
        fi
        if [ ! -d "${_sc_logdir}" ] ; then
            >&2 echo "Creating directory: [${_sc_logdir}]"
            mkdir -p "${_sc_logdir}"
        fi
        touch "${_sc_logfile}"
        >&2 echo "Rebuilding Applications DB..."
        RUN_LOG "bash './${_sc_name}' '${USERNAME}'"
        _rc=$?
        break
    done
    cd "${opwd}"
    #
    DIST_UPGRADE_ENABLED="${oDIST_UPGRADE_ENABLED}"
    return ${_rc}
}


MKDIR_TMP || _exit
INPUT="${TMP_DIR}/in"
OUTPUT="${TMP_DIR}/out"
TMPFILE="${TMP_DIR}/tmpfile"

MEMORY_K_TOTAL="$( free -k | awk '$1 == "Mem:" {print $2}' )"
DISK_K_FREE="$( df -k / | awk '$NF == "/" {print $4}' )"

# Cleanup procedure
trap "_exit_cls" SIGHUP SIGINT SIGTERM




#--------------------------------------------------------------------------------
# Main script
#--------------------------------------------------------------------------------

LSB_VENDOR="$(   lsb_release -si )"
LSB_RELEASE="$(  lsb_release -sr )"
LSB_CODENAME="$( lsb_release -sc )"

# LSB info "normalization"
case "${LSB_VENDOR}" in
  "CentOS"|"Debian")
      LSB_RELEASE="$( lsb_release -sr | awk -F'.' '{print $1}' )"
      ;;
  "Devuan")
      if [ "${LSB_RELEASE}" = "jessie" ] ; then
          LSB_RELEASE="1.0"
          LSB_CODENAME="jessie"
      fi
      ;;
esac
#
LSB_VER="${LSB_VENDOR}${LSB_RELEASE}"


#
# Prepare system packages repositories
#
case "${LSB_VER}" in
  "Debian8")
      cat <<-EOF > "/etc/apt/sources.list"
	deb http://deb.debian.org/debian/ ${LSB_CODENAME} main contrib non-free
	deb-src http://deb.debian.org/debian/ ${LSB_CODENAME} 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
      _apt_get_extra_opts=""
      ;;
  "Debian9"|"Ubuntu16.04")
      _apt_get_extra_opts=""
      ;;
  *)
      _apt_get_extra_opts="--allow-releaseinfo-change"
      ;;
esac


case "${LSB_VENDOR}" in
  "CentOS"|"CentOSStream")
      _pkg_ver="_rpm_ver"
      # Install required packages
      for _pkg in "dialog" "sqlite" ; do
          if ! rpm -q "${_pkg}" > /dev/null 2>&1 ; then
              yum -y install "${_pkg}" >> "${LOGFILE}" 2>&1
              main_rc=$?
              [ ${main_rc} -ne 0 ] && _exit
          fi
      done
      if [ "${LSB_RELEASE}" = "7" ] ; then
          if [ ! -x "/usr/local/bin/dialog" ] ; then
              rm -f "/usr/local/bin/dialog"            # just in case :)
              wget -nc -q --no-check-certificate \
                   -O "/usr/local/bin/dialog"    \
                   --timeout=20 --tries=5        \
                   "${RSB_URL}/support/dialog/CentOS7/dialog" >> "${LOGFILE}" 2>&1
              chmod -v 0755 "/usr/local/bin/dialog" >> "${LOGFILE}" 2>&1
          fi
      fi
      [ -z "${USERUID}" ] && USERUID="${FIRST_UID}"    # First run on "minimal" template
      if [ ${USERUID} -eq ${FIRST_UID} ] ; then
          yum list updates \
              | awk '/Updated Packages/,0' \
              | grep -v '^Updated Packages' \
              | awk '{print $1" "$2}' \
                 > "${TMP_DIR}/upgradable-pkgs.list"
      fi
      ;;

  "Debian"|"Devuan"|"Ubuntu")
      _pkg_ver="_dpkg_ver"
      #
      # Remove unsupported repositories
      #
      for __f in \
          "dawidd0811-ubuntu-neofetch-focal.list" \
          "izzysoft.list" \
          "getdeb.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
            ;;
        "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

      if ! dpkg -s "apt-transport-https" > /dev/null 2>&1 ; then
          apt-get -q -y ${_apt_get_extra_opts} update > /dev/null 2>> "${LOGFILE}"
          apt-get -q -y ${_apt_get_extra_opts} install "apt-transport-https" > /dev/null 2>> "${LOGFILE}"
          main_rc=$?
          if [ ${main_rc} -ne 0 ] ; then
              cat <<-EOF
		ERROR: Failed to install "apt-transport-https" package.
		Please look at the '${LOGFILE}' and recent
		 logfiles in '/var/log/whmcs_client_installers/' directory.
		EOF
              _exit
          fi
      fi

      if ! apt-get -q -y ${_apt_get_extra_opts} update > /dev/null 2>> "${LOGFILE}" ; then
          >&2 echo "Let's try to fix GPG keys issues..."
          apt-get ${_apt_get_extra_opts} update > /dev/null 2>> "${LOGFILE}" \
              | sed -ne 's/.*NO_PUBKEY //p' \
              | while read key; do
                    if ! [[ ${keys[*]} =~ ${key} ]] ; then
                        echo "Retrieving key ID: [${key}]"
                        apt-key adv --keyserver keyserver.ubuntu.com --recv-keys \"\${key}\" >> "${LOGFILE}"
                        keys+=("${key}")
                    fi
                done
          apt-get -q -y ${_apt_get_extra_opts} update > /dev/null 2>> "${LOGFILE}"
          main_rc=$?
          if [ ${main_rc} -ne 0 ] ; then
              cat <<-EOF
		ERROR: Failed to apdate APT packages database.
		Please look at the '${LOGFILE}' and recent
		 logfiles in '/var/log/whmcs_client_installers/' directory.
		EOF
              _exit
          fi
      fi

      # Generate list of system packages that can be upgraded
      [ -z "${USERUID}" ] && USERUID="${FIRST_UID}"    # First run on "minimal" template
      if [ "${USERUID}" -eq "${FIRST_UID}" ] ; then
          apt-get upgrade -s \
              | grep -e '^Inst' \
              | awk '{print $2" "$4}' \
              | tr -d '(' \
           > "${TMP_DIR}/upgradable-pkgs.list"
      fi

      # Install required packages
      for _pkg in "dialog" "sqlite3" ; do
          if ! dpkg -s "${_pkg}" > /dev/null 2>&1 ; then
              apt-get -y install "${_pkg}" >> "${LOGFILE}" 2>&1
              main_rc=$?
              [ ${main_rc} -ne 0 ] \
                  && _exit
          fi
      done
      ;;

  *)
      ERROR "Unsupported distribution: '${LSB_VENDOR} ${LSB_RELEASE}'"
      _exit
      ;;

esac

[ ! -s "${TMP_DIR}/upgradable-pkgs.list" \
    -a "${CLEAN_TMP}" != "yes" ] \
         && rm -f "${TMP_DIR}/upgradable-pkgs.list"


# Based on the idea of one guy :)
# https://stackoverflow.com/questions/22569195/is-it-possible-to-print-a-spinning-cursor-in-a-terminal-using-bash-scripting
spin () {
    local progress
    local -a marks=( '/' '-' '\' '|' )
    local i=0
    sleep 1
    while true ; do
        [ -e "${TMP_DIR}/db_rebuild_progress" ] \
            && progress="$( cat "${TMP_DIR}/db_rebuild_progress" )" \
            || progress=0
        [ -n "${progress}" ] \
            && printf '  --> %3d%% %s\r' ${progress} "${marks[i++ % ${#marks[@]}]}"
        sleep 1
    done
}


#
# Rebuild APP DB
#
spin_pid=""
if [ "${LIST_ONLY}" != "yes" ] && [ -z "${APP_LIST}" ] ; then
    spin &
    spin_pid=$!
fi

NEED_RESTART="no"
[ -z "${USERHOME}" ] && NEED_RESTART="yes"

_rebuild_db "auto"
_rc=$?

[ -n "${spin_pid}" ] && kill -9 ${spin_pid} > /dev/null 2>&1

if [ ${_rc} -ne 0 ] ; then
    case "${_rc}" in
      "116")
          echo "Resources shortage (f.e. low VPS total memory, not enough free disk space, etc)"
          ;;
    esac
    echo "RC: [${_rc}]"
    echo "Exiting..."
    exit ${_rc}
fi

if [ "${NEED_RESTART}" != "no" ] ; then
     >&2 echo "install-app: need to restart... Sorry for that :("
     rm -f "/var/lock/whmcs_client_installers.lock"
     >&2 exec "${0}" -n ${_SCRIPT_ARGS}
     exit 0
fi


# ----------------------------------------------------------------
# Batch application(s) installation / uninstallation / upgrade
# ----------------------------------------------------------------
if [ -n "${APP_LIST}" ] ; then
    _main_rc=0
    for _s in $( echo "${APP_LIST}" | tr ',' ' ' ) ; do
       _app="$( echo "${_s}" | awk -F ':' '{print $1}' | tr '[:upper:]' '[:lower:]' )"
       _op="$(  echo "${_s}" | awk -F ':' '{print $2}' | tr '[:upper:]' '[:lower:]' )"
       _app_id="$( sqlite3 "${APP_DB}" \
                             "SELECT id FROM application WHERE pkgname='${_app}';" )"
       if [ -z "${_app_id}" ] ; then
           echo "[${_app}]: application is not supported!"
           continue
       fi
       case "${_op}" in
         in*)
             _op="install"
             ;;
         un*)
             _op="uninstall"
             if [ "${_app}" = "dist_upgrade" ] ; then
                 continue
             fi
             ;;
         up*)
             _op="upgrade"
             if [ "${_app}" = "dist_upgrade" ] ; then
                 _op="install"
             fi
             ;;
         *)
             echo "[${_op}]: invalid operation requesed!"
             continue
             ;;
       esac
       echo -e "${_app}\t->\t${_op}"
       _status="$( sqlite3 "${APP_DB}" \
              "SELECT status FROM application WHERE pkgname='${_app}';" )"
       _version="$( sqlite3 "${APP_DB}" \
              "SELECT version FROM application WHERE pkgname='${_app}';" )"
       _is_upgradable="$( sqlite3 "${APP_DB}" \
              "SELECT is_upgradable FROM application WHERE pkgname='${_app}';" )"
       _upgrade_ver="$( sqlite3 "${APP_DB}" \
              "SELECT upgrade_ver FROM application WHERE pkgname='${_app}';" )"
       if [ "${_op}" = "install" ] ; then
           if [ "${_status}" = "1" ] ; then
               if [ "${_app}" != "dist_upgrade" ] ; then
                   echo "[${_app}]: already installed: [${_version}]"
                   continue
               else
                   if [ "${_upgrade_ver}" != "Updates available" ] ; then
                       echo "[${_app}]: no updates available"
                       continue
                   else
                       if [ -e "${TMP_DIR}/upgradable-pkgs.list" ] ; then
                           echo "Will install $( wc -l < "${TMP_DIR}/upgradable-pkgs.list" ) updates ..."
                           cat "${TMP_DIR}/upgradable-pkgs.list"
                       fi
                   fi
               fi
           fi
       elif [ "${_op}" = "uninstall" ] ; then
           if [ "${_status}" = "0" ] ; then
               echo "[${_app}]: is not installed"
               continue
           fi
       elif [ "${_op}" = "upgrade" ] ; then
           if [ "${_status}" = "0" ] ; then
               echo "[${_app}]: is not installed"
               continue
           fi
           if [ "${_is_upgradable}" != "yes" ] ; then
               echo "[${_app} ${_version}]: upgrade operation is not supported"
               continue
           else
               if [ -z "${_upgrade_ver}" ] ; then
                   echo "[${_app} ${_version}]: upgrade is not available"
                   continue
               elif [ "${_version}" = "${_upgrade_ver}" ] ; then
                   echo "[${_app}]: already has latest version: [${_version}]"
                   continue
               else
                   echo "[${_app} ${_version}]: will try to upgrade to the latest version: [${_upgrade_ver}]"
               fi
           fi
       fi
       _sc_name="${_op}_${_app}.sh"
       if ! WGET "${RSB_URL}/${_sc_name}" ; then
           echo "Error fetching ${_sc_name}"
           main_rc=1
           break
       fi
       opwd="$( pwd )"
       cd "${TMP_DIR}"
       echo "Running '${_sc_name}': please wait..."
       RUN_LOG "bash ${BASH_OPTS} './${_sc_name}' '${USERNAME}' '${USERPASSWORD}'"
       main_rc=$?
       cd "${opwd}"
       if [ "${main_rc}" != "0" ] ; then
           echo "Error running '${_sc_name}': RC [${main_rc}]"
           break
       else
           echo "${_sc_name}: Success: RC [${main_rc}]"
       fi
    done
    rm -rf "${TMP_DIR}"
    [ -e "/var/run/reboot-required" ] \
        && echo "*** REBOOT REQUIRED ***"
    exit ${main_rc}
fi


# ----------------------------------------------------------------
# LIST_ONLY operation
# ----------------------------------------------------------------
if [ "${LIST_ONLY}" = "yes" ] ; then
    red=$'\033[0;31m'
    nc=$'\033[0m'
    while read cat_id ; do
        cat_name="$( sqlite3 "${APP_DB}" \
                             "SELECT name FROM category WHERE id=${cat_id};" )"
        while read s ; do
            IFS='|'
            read -ra __arr <<< "${s}"
            IFS=' '
            id="${__arr[0]}"
            name="${__arr[1]}"
            if [ -n "${SEARCH}" ] ; then
                echo "${name}" | grep -q -i "${SEARCH}" \
                    || continue
            fi
            version="${__arr[2]}"
            is_upgradable="${__arr[3]}"
            upgrade_ver="${__arr[4]}"
            _status="${__arr[5]}"
            _pkgname="${__arr[6]}"
            echo -n "${cat_name}"
            if [ -n "${SEARCH}" -a "${NO_COLORS}" != "yes" ] ; then
                echo -n ": "
                echo -n "${name}" | sed "s/\(${SEARCH}\)/${red}\1${nc}/gI"
            else
                echo -n ": ${name}"
            fi
            echo -n ": ${_pkgname}"
            if [ "${_status}" = "1" ] ; then
                echo -n ": Installed"
                [ ! -z "${version}" ] \
                    && echo -n " (${version})"
                if [ "${is_upgradable}" = "yes" ] ; then
                    if [ ! -z "${upgrade_ver}" ] ; then
                        if [ "${upgrade_ver}" != "${version}" ] ; then
                            echo -n ": Upgradable to ${upgrade_ver}"
                        fi
                    fi
                fi
            fi
            echo -ne "\n"
        done < <(sqlite3 "${APP_DB}" \
            "SELECT id,name,version,is_upgradable,upgrade_ver,status,pkgname \
             FROM application \
             WHERE cat_id=${cat_id} \
             ORDER BY name COLLATE NOCASE ASC;")
    done < <(sqlite3 "${APP_DB}" \
        "SELECT id FROM category ORDER BY name COLLATE NOCASE ASC;")
    _exit
fi 


MENU_DB="${TMP_DIR}/menu.db"
_rebuild_menu () {
    local __arr
    local app_cnt app_idx
    local cat_id cat_name
    local id
    local is_upgradable
    local menu2
    local _help
    local name modified_name
    local scope
    local pkgname
    local _status
    local extra_vars
    local upgrade_ver
    local upgrade_menu2
    local version
    local oIFS
    local tmp_file
    local s
    local _s
    local _reboot_req_msg=""
    local _password

    [ -e "${MENU_DB}" ] && RM_FILES "${MENU_DB}"

    [ -e "/var/run/reboot-required" ] \
        && _reboot_req_msg="\Zb\Z1REBOOT REQUIRED\Zn"

    sqlite3 "${MENU_DB}" <<-EOF
	CREATE TABLE application (
	      operation     TEXT,
	      id            INTEGER,
	      cat_name      TEXT,
	      pkgname       TEXT,
	      menu1         TEXT,
	      menu2         TEXT,
	      help          TEXT
	    );
	EOF

    if [ "${USERNAME}" = "user" ] ; then
        app_cnt="$( sqlite3 "${APP_DB}" \
                "SELECT COUNT(*) FROM application;" )"
    else
        app_cnt="$( sqlite3 "${APP_DB}" \
                "SELECT COUNT(*) FROM application WHERE scope = 'user';" )"
    fi
    app_idx=0
    {
    while read s ; do  
        oIFS="${IFS}"
        IFS='|'
        read -ra __arr <<< "${s}"
        IFS="${oIFS}"
        id="${__arr[0]}"
        cat_id="${__arr[1]}"
        name="${__arr[2]}"
        if [ -n "${SEARCH}" ] ; then
            echo "${name}" | grep -q -i "${SEARCH}" || continue
        fi
        pkgname="${__arr[3]}"
        version="${__arr[4]}"
        is_upgradable="${__arr[5]}"
        upgrade_ver="${__arr[6]}"
        _status="${__arr[7]}"
        extra_vars="${__arr[8]}"
        scope="${__arr[9]}"
        [    "${USERNAME}" != "user" \
          -a "${scope}" != "user" ] &&  continue

        cat_name="$( sqlite3 "${APP_DB}" \
                       "SELECT name FROM category WHERE id=${cat_id};" )"
        app_idx="$(( app_idx + 1 ))"
        cat <<-EOF
		XXX
		$((100 * app_idx / app_cnt))

		${app_idx}/${app_cnt}: ${cat_name} -> ${name}
		XXX
		EOF

        # 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
        DAEMON_PORT=""
        WEBUI_PORT=""
        if [ ! -z "${extra_vars}" ] ; then
            eval "${extra_vars}"
        fi

        # Generate --item-help value for an application
        _help="$( sqlite3 "${APP_DB}" "SELECT about FROM application WHERE id=${id};" \
                   | grep -m1 -e "^Access URL:" \
                   | sed -e 's/^Access URL:\s*\(.*\)$/\1/' )"
        [ "${USERNAME}" = "user" ] \
            && _help="$( echo "${_help}" | sed -e "s/^\(.*\)-###USERNAME###$/\1/g" )"
        [ -n "${DAEMON_PORT}" ] \
            && _help="$( echo "${_help}" \
                            | sed -e "s/###DAEMON_PORT###/$(( USERUID + DAEMON_PORT - FIRST_UID ))/g" )"
        [ -n "${WEBUI_PORT}" ] \
            && _help="$( echo "${_help}" \
                            | sed -e "s/###WEBUI_PORT###/$(( USERUID + WEBUI_PORT - FIRST_UID ))/g" )"
        _help="$( echo "${_help}" | sed -e "s|###IP###|${HTTPS_HOST}|g" )"
        _help="$( echo "${_help}" | sed -e "s|###USERHOME###|${USERHOME}|g" )"
        _help="$( echo "${_help}" | sed -e "s|###USERNAME###|${USERNAME}|g" )"
        _help="$( echo "${_help}" | sed -e "s|###USERPASSWORD###|${USERPASSWORD}|g" )"

        # - Install operation:
        menu2=""
        modified_name="${name}"
        if [ "${_status}" = "1" ] ; then
            # -- Application is installed
            case "${pkgname}" in
              "dist_upgrade")
                  menu2="$( echo "${version}" | awk -F "-" "{print \$1}" )"
                  if [ -n "${upgrade_ver}" ] ; then
                      menu2="${menu2}, ${upgrade_ver}"
                      _help="${upgrade_ver}"
                      if [ "${upgrade_ver}" = "Updates available" ] ; then
                          if [ -e "${TMP_DIR}/upgradable-pkgs.list" ] ; then
                              _help="$( wc -l < "${TMP_DIR}/upgradable-pkgs.list" ) updates available"
                          fi
                      fi
                  fi
                  ;;
              *)
                  menu2="Installed"
                  [ ! -z "${version}" ] \
                      && menu2="${menu2} ($( echo "${version}" | awk -F "-" "{print \$1}" ))"
                  ;;
            esac
            if    [ "${_status}" = "1" ] \
               && [ "${pkgname}" != "dist_upgrade" ] \
               && [ "${is_upgradable}" = "yes" ] \
               && [ -n "${upgrade_ver}" ] \
               && [ "${upgrade_ver}" != "${version}" ]
            then
                 modified_name="${name} [*]"
            fi
        else
            # -- Application is not installed
            # -- Let's check if we can install it or not (resource requirements, etc)
            if [ -n "${MEMORY_K_TOTAL}" -a -n "${MEMORY_K_MIN}" ] ; then
                [ ${MEMORY_K_TOTAL} -lt ${MEMORY_K_MIN} ] \
                    && continue
            fi
            if [ -n "${DISK_K_FREE}" -a -n "${DISK_K_MIN}" ] ; then
                [ ${DISK_K_FREE} -lt ${DISK_K_MIN} ] \
                    && continue
            fi
        fi
        #
        sqlite3 "${MENU_DB}" \
            "INSERT INTO application \
                ( operation, id, cat_name, pkgname, menu1, menu2, help ) \
             VALUES \
                ( 'install', '${id}', '${cat_name}', '${pkgname}', '${modified_name}', '${menu2}', '$( _menu_item_help "${_help}" "${_reboot_req_msg}" )' );"

        # - Upgrade operation:
        if [ "${_status}" = "1" -a "${is_upgradable}" = "yes" ] ; then
            if [ ! -z "${upgrade_ver}" -a "${version}" != "${upgrade_ver}" ] ; then
                case "${name}" in
                  "OS Update")
                      [ "${upgrade_ver}" = "Up to date" ] && continue
                      upgrade_menu2="${version}, ${upgrade_ver}"
                      ;;
                  *)
                      upgrade_menu2="Upgrade to ${upgrade_ver}"
                      ;;
                esac
                [ "${pkgname}" != "dist_upgrade" ] \
                    && _help="Upgrade \Zu\Z5${name}\Zn: ${version} -> \Z2${upgrade_ver}\Zn"
                sqlite3 "${MENU_DB}" \
                    "INSERT INTO application \
                        ( operation, id, cat_name, pkgname, menu1, menu2, help ) \
                     VALUES \
                        ( 'upgrade', '${id}', '${cat_name}', '${pkgname}', '${name}', \
                          '${upgrade_menu2}', '$( _menu_item_help "${_help}" "${_reboot_req_msg}" )' );"
            fi
        fi

        # - Uninstall operation:
        menu2=""
        [ "${name}" = "OS Update" ] && continue
        if [ "${_status}" = "1" ] ; then
            _help="${name}"
            menu2=""
            if [ ! -z "${version}" ] ; then
                _help="${_help} ${version}"
                menu2="$( echo "${version}" | awk -F "-" "{print \$1}" )"
            fi
            sqlite3 "${MENU_DB}" \
                "INSERT INTO application \
                    ( operation, id, cat_name, pkgname, menu1, menu2, help ) \
                 VALUES \
                    ( 'uninstall', '${id}', '${cat_name}', '${pkgname}', '${name}', '${menu2}', '$( _menu_item_help "${_help}" "${_reboot_req_msg}" )' );"
        fi

    done < <(sqlite3 "${APP_DB}" \
         "SELECT id,cat_id,name,pkgname,version,is_upgradable,upgrade_ver,status,extra_vars,scope FROM application;")
    } | dialog ${DIALOG_OPTS} \
          --backtitle "$( _backtitle "Updating applications menu" )" \
          --title " Updating applications menu " \
          --gauge "Please wait..." \
          10 70 0

    return 0
}


>&2 echo "Building Applications menu..."
_rebuild_menu

# -- !!! TBD !!! -- #
#>&2 echo -e "\nDone!"
#>&2 echo "Ready to start UI!"

default_op="Install Application"
while true ; do

    if [ "${DEBUG}" = "yes" ] ; then
        dbg_entry="Disable debugging"
    else
        dbg_entry="Enable debugging"
    fi

    if [ "${DIST_UPGRADE_ENABLED}" = "yes" ] ; then
        dup_entry="Disable Dist-Upgrade"
    else
        dup_entry="Enable Dist-Upgrade"
    fi

    export DEBUG
    export DIST_UPGRADE_ENABLED

    num_upgradable="$( sqlite3 "${MENU_DB}" \
                         "SELECT COUNT(*) FROM application WHERE operation = 'upgrade';" )"
    num_uninstallable="$( sqlite3 "${MENU_DB}" \
                         "SELECT COUNT(*) FROM application WHERE operation = 'uninstall';" )"
    REBOOT_REQUIRED_MSG=""
    APPS_STATS=""
    [ -e "/var/run/reboot-required" ] \
        && REBOOT_REQUIRED_MSG="\Zb\Z1REBOOT REQUIRED\Zn"
    if [ "${USERUID}" = "${FIRST_UID}" ] ; then
        APPS_STATS="installed: $( \
               sqlite3 "${APP_DB}" "SELECT COUNT(*) FROM application WHERE status = 1;" \
            ), available: $( \
               sqlite3 "${APP_DB}" "SELECT COUNT(*) FROM application;" \
            )"
        if [ "${num_upgradable}" != "0" ] ; then
            APPS_STATS="${APPS_STATS}, updates available: ${num_upgradable}"
        fi
    else
        APPS_STATS="installed: $( \
               sqlite3 "${APP_DB}" "SELECT COUNT(*) FROM application WHERE status = 1 AND scope = 'user';" \
            ), available: $( \
               sqlite3 "${APP_DB}" "SELECT COUNT(*) FROM application WHERE scope = 'user';" \
            )"
    fi
    main_menu_item_help="$( _menu_item_help "IP: \Zu\Z5${IP}\Zn - ${APPS_STATS}" "${REBOOT_REQUIRED_MSG}" )"
    cat <<-EOF > "${TMP_DIR}/main_menu.sh"
	dialog \${DIALOG_OPTS} \\
	               --backtitle "\$( _backtitle "Manage applications")" \\
	               --title " [ M A I N - M E N U ] " \\
	               --item-help \\
	               --cancel-label "Exit" \\
	               --default-item "\${default_op}" \\
	               --menu "You can use the UP/DOWN arrow keys, the first \\n\\
	letter of the choice as a hot key, or the \\n\\
	number keys 1-9 to choose an option.\\n\\
	Choose the TASK" \\
	                0 0 0 \\
	                "Install Application" "" "${main_menu_item_help}" \\
	EOF
    if [ ${num_uninstallable} -ne 0 ] ; then
    cat <<-EOF >> "${TMP_DIR}/main_menu.sh"
	                "Uninstall Application" "" "${main_menu_item_help}" \\
	EOF
    fi
    if [ ${num_upgradable} -ne 0 ] ; then
        if [ ${USERUID} -eq ${FIRST_UID} ] ; then
            cat <<-EOF >> "${TMP_DIR}/main_menu.sh"
	                "Upgrade Application" "" "${main_menu_item_help}" \\
		EOF
        fi
    fi
    cat <<-EOF >> "${TMP_DIR}/main_menu.sh"
	                "\${dbg_entry}" "" "${main_menu_item_help}" \\
	                "\${dup_entry}" "" "${main_menu_item_help}" \\
	                "Exit" "" "${main_menu_item_help}" \\
	            2>"\${INPUT}"
	EOF
    . "${TMP_DIR}/main_menu.sh"
    ret=$?

    [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} ] && break

    operation="$( cat "${INPUT}" )"
    default_op="${operation}"

    case "${operation}" in
      "Install Application")
          op="install"
          ;;
      "Uninstall Application")
          op="uninstall"
          ;;
      "Upgrade Application")
          op="upgrade"
          ;;
      "Disable debugging")
          default_op="Enable debugging"
          DEBUG="no"
          BASH_OPTS=""
          continue
          ;;
      "Enable debugging")
          default_op="Disable debugging"
          DEBUG="yes"
          BASH_OPTS="-xv"
          continue
          ;;
      "Disable Dist-Upgrade")
          default_op="Enable Dist-Upgrade"
          DIST_UPGRADE_ENABLED="no"
          continue
          ;;
      "Enable Dist-Upgrade")
          default_op="Disable Dist-Upgrade"
          DIST_UPGRADE_ENABLED="yes"
          continue
          ;;
      "Exit")
          break
          ;;
    esac

    default_cat=""
    real_op="${op}"
    while true ; do

        ar_cat=()
        while read -r s ; do
            _num_upgradable="0"
            _cat_menu_name="${s}"
            cat_id="$( sqlite3 "${APP_DB}" "SELECT id FROM category WHERE name = '${s}';" )"
            if [ "${op}" = "upgrade" ] ; then
                _num_upgradable="$( sqlite3 "${MENU_DB}" \
                     "SELECT COUNT(*) FROM application WHERE cat_name = '${s}' AND operation = 'upgrade';" )"
                APPS_STATS="updates available: ${_num_upgradable}"
            else
                if [ "${USERUID}" = "${FIRST_UID}" ] ; then
                    APPS_STATS="installed: $( \
                        sqlite3 "${APP_DB}" \
                              "SELECT COUNT(*) FROM application WHERE cat_id = ${cat_id} AND status = 1;" \
                          )"
                    if [ "${op}" = "install" ] ; then
                        APPS_STATS="${APPS_STATS}, available: $( \
                                        sqlite3 "${APP_DB}" \
                                               "SELECT COUNT(*) FROM application WHERE cat_id = ${cat_id};" \
                                         )"
                        _num_upgradable="$( sqlite3 "${MENU_DB}" \
                             "SELECT COUNT(*) FROM application WHERE cat_name = '${s}' AND operation = 'upgrade';" )"
                        if [ "${_num_upgradable}" != "0" ] ; then
                            APPS_STATS="${APPS_STATS}, upgradable: ${_num_upgradable}"
                        fi
                    fi
                else
                    APPS_STATS="installed: $( \
                        sqlite3 "${APP_DB}" \
                              "SELECT COUNT(*) FROM application WHERE cat_id = ${cat_id} AND status = 1 AND scope = 'user';" \
                          ), available: $( \
                        sqlite3 "${APP_DB}" \
                              "SELECT COUNT(*) FROM application WHERE cat_id = ${cat_id} AND scope = 'user';" \
                          )"
                fi
            fi
            if [ "${_num_upgradable}" != "0" ] && [ "${op}" = "install" ] ; then
                _cat_menu_name="${_cat_menu_name} [!]"
            else
                if grep -q -E "^${_cat_menu_name} \[" <<< "${default_cat}" ; then
                    default_cat="${_cat_menu_name}"
                fi
            fi
            ar_cat+=( "${_cat_menu_name}" "" "$( _menu_item_help "${APPS_STATS}" "${REBOOT_REQUIRED_MSG}" )" )
        done < <( sqlite3 ${MENU_DB} \
                   "SELECT DISTINCT cat_name FROM application WHERE operation = '${op}' ORDER BY cat_name;" )

        num_cat="${#ar_cat[@]}"
        if [ ${num_cat} -eq 0 ] ; then
            default_cat=""
            break
        fi

        num_cat="$(( num_cat / 2 ))"
        [ ${num_cat} -gt 15 ] && h_cat=15 || h_cat="${num_cat}"

        dialog ${DIALOG_OPTS} \
                --backtitle "$( _backtitle "${operation}" )" \
                --title " ${op^}: Choose category " \
                --item-help \
                --extra-button \
                --extra-label "Exit" \
                --cancel-label " << Back" \
                --default-item "${default_cat}" \
                --menu "You can use the UP/DOWN arrow keys, the first \n\
letter of the choice as a hot key, or the \n\
number keys 1-9 to choose an option.\n\
Choose the TASK" \
                0 0 0 \
                "${ar_cat[@]}" 2> "${INPUT}"
        ret=$?
        [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} ] && break
        [ ${ret} -eq ${DIALOG_EXTRA} ] && break 2
#        cat_name="$( cat "${INPUT}" )"
#        default_cat="${cat_name}"
        cat_menu_name="$( cat "${INPUT}" )"
        cat_name="$( echo "${cat_menu_name}" | sed -re 's/\s+\[\!\]$//' )"
        default_cat="${cat_menu_name}"

        default_app=""
        ok_button_label="$( echo "${operation}" | awk '{print $1}' )" \
        default_button="ok"
        while true ; do
            real_op="${op}"

            [ -e "/var/run/reboot-required" ] \
                && REBOOT_REQUIRED_MSG="\Zb\Z1REBOOT REQUIRED\Zn"

            ar_app=()
            while read -r s ; do
                IFS='|'
                read -ra __arr <<< "${s}"
                IFS=' '
                ar_app+=( "${__arr[0]}" \
                          "${__arr[1]}" \
                          "$( _menu_item_help "${__arr[2]}" "${REBOOT_REQUIRED_MSG}" )" )
            done < <( sqlite3 "${MENU_DB}" \
                       "SELECT menu1,menu2,help \
                        FROM application \
                        WHERE operation = '${op}' AND cat_name = '${cat_name}' \
                        ORDER BY menu1 COLLATE NOCASE ASC;" )

            num_app="${#ar_app[@]}"                  
            if [ ${num_app} -eq 0 ] ; then
                default_cat=""
                break
            fi

            num_app="$(( num_app / 3 ))"
            [ ${num_app} -gt 15 ] && h_app=15 || h_app="${num_app}"

            dialog ${DIALOG_OPTS} \
                --backtitle \
                    "$( _backtitle "${operation}: ${cat_name}" )" \
                --title \
                    " ${op^}: ${cat_name}: Choose application " \
                --ok-label "${ok_button_label}" \
                --extra-button \
                --extra-label "Exit" \
                --cancel-label " << Back" \
                --item-help \
                --help-button \
                --help-tags \
                --help-label "About" \
                --default-button "${default_button}" \
                --default-item "${default_app}" \
                --menu "You can use the UP/DOWN arrow keys, the first \n\
letter of the choice as a hot key, or the \n\
number keys 1-9 to choose an option.\n\
Choose the TASK" \
                0 0 0 \
                "${ar_app[@]}" 2> "${INPUT}"
            ret=$?
            [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} ] && break
            [ ${ret} -eq ${DIALOG_EXTRA} ] && break 3

            # Help (About) is special case
            if [ ${ret} -eq ${DIALOG_HELP} ] ; then
                app_menu_name="$( sed -e 's/HELP \(.*\)/\1/' "${INPUT}" )"
                app_name="$( echo "${app_menu_name}" | sed -re 's/\s+\[\*\]//' )"
                default_button="help"
                default_app="${app_menu_name}"
                extra_vars="$( sqlite3 "${APP_DB}" \
                        "SELECT extra_vars FROM application WHERE name = '${app_name}';" )"
                DAEMON_PORT=""
                WEBUI_PORT=""
                RUTORRENT_DIRECTORY=""
                APP_HOME=""
                MEMORY_K_MIN=""
                PLUGINS_DIRECTORY=""
                APP_DIRECTORY=""
                DELUGE_VER=""
                RT_S_VER=""
                SYS_PKG_NAME=""
                STORED_PASSWORD_SERVICE=""
                if [ ! -z "${extra_vars}" ] ; then
                    eval "${extra_vars}"
                fi
                about=""
                if [ "${app_name}" != "${app_menu_name}" ] ; then
                    about="!!! UPDATE to version $( sqlite3 "${APP_DB}" \
                        "SELECT upgrade_ver FROM application WHERE name = '${app_name}';" \
                          ) is AVAILABLE !!!
----------------------------------------------------------------------

"
                fi
                about="${about}$( sqlite3 "${APP_DB}" \
                   "SELECT about FROM application WHERE name = '${app_name}';" )"
                if [ "${USERNAME}" = "user" ] ; then
                    about="$( echo "${about}" \
                        | sed -e "s/^\(Access URL:.*\)-###USERNAME###$/\1/g" )"
                    about="$( echo "${about}" \
                        | sed -e "s/^\(Password:.*\`changeseedboxpass\) -u ###USERNAME###.*\(\`.*\)$/\1\2/g" )"
                fi
                [ -n "${DAEMON_PORT}" ] \
                    && about="$( echo "${about}" \
                         | sed -e "s/###DAEMON_PORT###/$(( USERUID + DAEMON_PORT - FIRST_UID ))/g" )"
                [ -n "${WEBUI_PORT}" ] \
                    && about="$( echo "${about}" \
                         | sed -e "s/###WEBUI_PORT###/$(( USERUID + WEBUI_PORT - FIRST_UID ))/g" )"
                about="$( echo "${about}" | sed -e "s/###IP###/${HTTPS_HOST}/g" )"
                about="$( echo "${about}" | sed -e "s|###USERHOME###|${USERHOME}|g" )"
                about="$( echo "${about}" | sed -e "s/###USERNAME###/${USERNAME}/g" )"
                about="$( echo "${about}" | sed -e "s/###DAEMON_PORT###/${DAEMON_PORT}/g" )"
                about="$( echo "${about}" | sed -e "s|###APP_D###|${APP_DIRECTORY}|g" )"
                RPC_SECRET="You'll see real RPC tocken here after the installation!"
                if [ -e "${APP_DIRECTORY}/.rpc_secret" ] ; then
                    RPC_SECRET="$( cat "${APP_DIRECTORY}/.rpc_secret" )"
                fi
                about="$( echo "${about}" | sed -e "s|###RPC_SECRET###|${RPC_SECRET}|g" )"
                if [ ! -e "${PASSWORDS_STORE}" -a -n "${STORED_PASSWORD_SERVICE}" ] ; then
                    about="$( echo "${about}" \
                                  | sed -e "s|###USERPASSWORD###|${USERPASSWORD}|g" )"
                else
                    _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}" )"
                    [ -n "${_password}" ] \
                        && _password="${_password} "
                    about="$( echo "${about}" \
                                  | sed -e "s|###USERPASSWORD###|${_password}|g" )"
                fi
                [ -z "${about}" ] && about="\n\n       Coming soon :)       \n\n"
                echo "${about}" > "${OUTPUT}"
                display_output 0 0 \
                         "  ${app_name}  " \
                         "About '${app_name}'"
                continue
            fi

            app_menu_name="$( cat "${INPUT}" )"
            app_name="$( echo "${app_menu_name}" | sed -re 's/\s+\[\*\]$//' )"
            default_app="${app_menu_name}"
            default_button="ok"
            pkgname="$( sqlite3 "${MENU_DB}" \
                   "SELECT pkgname FROM application WHERE operation = '${op}' AND menu1 = '${app_menu_name}';" )"
            b_title="$( echo "${operation}" \
                            | awk '{print $1}' ): ${cat_name} -> ${app_name}"
            case "${pkgname}" in
              "dist_upgrade")
                  [    ! -e "${TMP_DIR}/upgradable-pkgs.list" \
                    -o ! -s "${TMP_DIR}/upgradable-pkgs.list" ] \
                        && continue
                  dialog ${DIALOG_OPTS} \
                       --aspect 14 \
                       --backtitle "$( _backtitle "${b_title}" )" \
                       --title "  Progress  " \
                       --infobox "\n\n       Gathering updates information...       \n\n" \
                       7 60
                  arr_up=()
                  while read -r s ; do
                      read -ra __arr <<< "${s}"
                      arr_up+=( \
                          "${__arr[0]}" \
                          "${__arr[1]}" \
                          "Upgrade \Zu\Z5${__arr[0]}\Zn: $( ${_pkg_ver} ${__arr[0]} ) -> \Z2${__arr[1]}\Zn" \
                              )
                  done < "${TMP_DIR}/upgradable-pkgs.list"
                  dialog ${DIALOG_OPTS} \
                         --backtitle "$( _backtitle "${b_title}" )" \
                         --ok-label "Install updates" \
                         --item-help \
                         --menu "The following packages will be updated" \
                         0 0 0 \
                         "${arr_up[@]}" 2> "${INPUT}"
                  ret=$?
                  [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} ] \
                      && continue
                  ;;
            esac
            _status="$( sqlite3 "${APP_DB}" \
                    "SELECT status FROM application WHERE name = '${app_name}';" )"
            old_app_version="$( sqlite3 "${APP_DB}" \
                    "SELECT version FROM application WHERE name = '${app_name}';" )"
            new_app_version="$( sqlite3 "${APP_DB}" \
                    "SELECT upgrade_ver FROM application WHERE name = '${app_name}';" )"

            case "${op}" in
              "install")
                  if [    "${pkgname}" != "dist_upgrade" \
                       -a "${_status}" != "0" ]
                  then
                      # -- Upgrade available?
                      if [ "${app_name}" != "${app_menu_name}" ] ; then
                          real_op="upgrade"
                          b_title="Upgrade: ${cat_name} -> ${app_name}"
                          dialog ${DIALOG_OPTS} \
                                 --backtitle "$( _backtitle "${b_title}" )" \
                                 --yesno "Are you sure you want to upgrade \n${app_name} from ${old_app_version} to ${new_app_version} ?" \
                                 0 0 \
                              || continue
                      else
                          cat <<-EOF > "${OUTPUT}"

				${app_name} (${old_app_version}): latest version is already installed!

				EOF
                          display_output 0 0 \
                                "  ${app_name}  " \
                                "${b_title}"
                          continue
                      fi
                  fi
                  ;;
              "uninstall")
                  dialog ${DIALOG_OPTS} \
                         --defaultno \
                         --backtitle "$( _backtitle "${b_title}" )" \
                         --yesno "Are you sure you want to uninstall\n${app_name} ${old_app_version} ?" \
                         0 0 \
                      || continue
                  ;;
            esac

            time_start="$( date -u "+%s")"
            _sc_name="${real_op}_${pkgname}.sh"
            _sc_logdir="/var/log/whmcs_client_installers"
            _sc_logfile="${_sc_logdir}/${_sc_name}.log"
            dialog ${DIALOG_OPTS} \
                     --aspect 14 \
                     --backtitle "$( _backtitle "${b_title}" )" \
                     --title "  Progress  " \
                     --infobox "   Fetching '${app_name}' script...   " \
                     7 60
            [ -e "${TMP_DIR}/${_sc_name}" ] \
                && RM_FILES "${TMP_DIR}/${_sc_name}"
            if ! WGET "${RSB_URL}/${_sc_name}" ; then
                echo "!!! ..Error.. !!!" > "${OUTPUT}"
                display_output \
                   0 0 \
                   "  Results  " \
                   "${real_op^}: ${cat_name} -> ${app_name}: ${_sc_name}: Script not found"
                break
            fi
            [ ! -s "${TMP_DIR}/${_sc_name}" ] && break
            opwd="$( pwd )"
            cd "${TMP_DIR}"
            [ ! -d "${_sc_logdir}" ] && mkdir -p "${_sc_logdir}"
            touch "${_sc_logfile}"
            dialog ${DIALOG_OPTS} \
                     --backtitle "$( _backtitle "${b_title}" )" \
                     --title " Running '${app_name}' script " \
                     --hline "${_sc_name}" \
                     --exit-label " ...Please wait... " \
                     --tailbox \
                     "${_sc_logfile}" \
                     "${max_lines}" "${max_columns}" &
            RUN_LOG "echo $! > ${TMPFILE}"
            RUN_LOG "bash ${BASH_OPTS} './${_sc_name}' '${USERNAME}' '${USERPASSWORD}'"
            main_rc=$?
            RUN_LOG "kill $( cat "${TMPFILE}" )"
            [ "${CLEAN_TMP}" = "yes" ] \
                && RM_FILES "${TMP_DIR}/${_sc_name}"
            if [ ${main_rc} -ne 0 ] ; then
                time_end="$( date -u "+%s" )"
                time_spent="$(( time_end - time_start ))"
                str_time_spent="Execution time: $( str_time_format "${time_spent}" )"
                dialog ${DIALOG_OPTS} \
                     --aspect 14 \
                     --backtitle "$( _backtitle "${b_title}" )" \
                     --hline "${str_time_spent}" \
                     --title " Error: '${app_name}' script. Rc: [${main_rc}]" \
                     --msgbox \
                     "$( tail -200 "${_sc_logfile}" )" \
                     0 0
                echo -e "!!!..Failure..!!!\nRc: [${main_rc}]\n" > "${OUTPUT}"
                if [ ${main_rc} -eq 111 ] ; then
                    echo -e "'${app_name}': already installed !\n" >> "${OUTPUT}"
                    new_app_version="$( \
                        sqlite3 "${APP_DB}" \
                            "SELECT version FROM application WHERE name='${app_name}';" )"
                    new_app_version_short="$( \
                        echo "${new_app_version}" \
                            | awk -F "-" "{print \$1}" )"
                    menu2="Installed"
                    [ ! -z "${new_app_version_short}" ] \
                        && menu2="${menu2} (${new_app_version_short})"
                    sqlite3 "${MENU_DB}" \
                       "UPDATE application \
                        SET menu2 = '${menu2}' \
                        WHERE operation = '${op}' AND menu1 = '${app_menu_name}';" \
                          >> "${LOGFILE}" 2>&1
                    sqlite3 "${MENU_DB}" \
                       "DELETE FROM application \
                        WHERE operation = 'uninstall' AND menu1 = '${app_menu_name}';" \
                          >> "${LOGFILE}" 2>&1
                elif [ ${main_rc} -eq 112 ] ; then
                    echo -e "'${app_name}': is not installed !\n" >> "${OUTPUT}"
                fi
                [ -e "${TMP_DIR}/install-app.msg" ] \
                    && echo -ne "$( cat "${TMP_DIR}/install-app.msg" )\n\n" >> "${OUTPUT}"
                echo -e "Errors encountered.\nPlease see:\n${LOGFILE}\n${_sc_logfile}\nfor details !" \
                     >> "${OUTPUT}"
            else
                default_app="${app_name}"
                new_app_version="$( sqlite3 "${APP_DB}" \
                                       "SELECT version FROM application WHERE name='${app_name}';" )"
                new_app_version_short="$( echo "${new_app_version}" | awk -F "-" "{print \$1}" )"
                is_upgradable="$( sqlite3 "${APP_DB}" \
                                       "SELECT is_upgradable FROM application WHERE name='${app_name}';" )"
                upgrade_ver="$( sqlite3 "${APP_DB}" \
                                       "SELECT upgrade_ver FROM application WHERE name='${app_name}';" )"
                app_id="$( sqlite3 "${MENU_DB}" \
                                       "SELECT id FROM application \
                                            WHERE menu1='${app_menu_name}' AND operation = '${op}';" )"

                echo "***..Success..***" > "${OUTPUT}"

                case "${op}" in

                  "install")
                      echo "${app_name} ${new_app_version} installed" >> "${OUTPUT}"
                      menu2="Installed"
                      [ ! -z "${new_app_version_short}" ] \
                          && menu2="${menu2} (${new_app_version_short})"
                      sqlite3 "${MENU_DB}" \
                         "UPDATE application \
                          SET menu2 = '${menu2}' \
                          WHERE operation = '${op}' AND menu1 = '${app_menu_name}';" \
                            >> "${LOGFILE}" 2>&1
                      sqlite3 "${MENU_DB}" \
                         "DELETE FROM application \
                          WHERE operation = 'uninstall' AND menu1 = '${app_menu_name}';" \
                            >> "${LOGFILE}" 2>&1
                      sqlite3 "${MENU_DB}" \
                         "INSERT INTO application \
                          ( operation, id, cat_name, \
                            pkgname, menu1, \
                            menu2 ) \
                          VALUES \
                          ( 'uninstall', '${app_id}', '${cat_name}', \
                            '${pkgname}', '${app_menu_name}', \
                            '${new_app_version_short}' );" \
                            >> "${LOGFILE}" 2>&1
                      if [ "${is_upgradable}" = "yes" \
                             -a ! -z "${upgrade_ver}" \
                             -a "${upgrade_ver}" != "${new_app_version}" ] ; then
                          sqlite3 "${MENU_DB}" \
                             "DELETE FROM application \
                              WHERE operation = 'upgrade' AND menu1 = '${app_menu_name}';" \
                                >> "${LOGFILE}" 2>&1
                          sqlite3 "${MENU_DB}" \
                             "INSERT INTO application \
                              ( operation, id, cat_name, \
                                pkgname, menu1, menu2 ) \
                              VALUES \
                              ( 'upgrade', '${app_id}', '${cat_name}', \
                                '${pkgname}', '${app_name}', 'Upgrade to ${upgrade_ver}' );" \
                                >> "${LOGFILE}" 2>&1
                      fi
                      ;;

                  "uninstall")
                      echo "${app_name} ${old_app_version} uninstalled" >> "${OUTPUT}"
                      sqlite3 "${MENU_DB}" \
                         "DELETE FROM application \
                          WHERE operation = '${op}' AND menu1 = '${app_menu_name}';" \
                            >> "${LOGFILE}" 2>&1
                      sqlite3 "${MENU_DB}" \
                         "UPDATE application SET menu2 = '' \
                          WHERE operation = 'install' AND menu1 = '${app_menu_name}'" \
                            >> "${LOGFILE}" 2>&1
                      sqlite3 "${MENU_DB}" \
                         "DELETE FROM application \
                          WHERE operation = 'upgrade' AND menu1 = '${app_menu_name}';" \
                            >> "${LOGFILE}" 2>&1
                      #
                      # Uninstall dependent applications:
                      while read __id ; do
                          _pkgname="$( sqlite3 "${MENU_DB}" \
                              "SELECT pkgname FROM application \
                               WHERE operation = 'uninstall' AND id='${__id}';" )"
                          [ -z "${_pkgname}" ] && continue
                          _app_menu_name="$( sqlite3 "${MENU_DB}" \
                              "SELECT menu1 FROM application \
                               WHERE operation = 'uninstall' AND id='${__id}';" )"
                          _cat_name="$( sqlite3 "${MENU_DB}" \
                              "SELECT cat_name FROM application \
                               WHERE operation = 'uninstall' AND id='${__id}';" )"
                          __sc_name="uninstall_${_pkgname}.sh"
                          __sc_logfile="${_sc_logdir}/${__sc_name}.log"
                          _b_title="$( \
                              echo "${operation}" \
                                  | awk '{print $1}' \
                                     ): ${_cat_name} -> ${_app_menu_name}"
                          dialog ${DIALOG_OPTS} \
                              --aspect 14 \
                              --backtitle "$( _backtitle "${_b_title}" )" \
                              --title "  Progress  " \
                              --infobox "Fetching '${_app_menu_name}' script... " \
                              0 0
                          if ! WGET "${RSB_URL}/${__sc_name}" ; then
                              echo "!!! ..Error.. !!!" > "${OUTPUT}"
                              display_output 0 0 \
                                  "  Results  " \
                                  "${_b_title}"
                              continue
                          fi
                          [ ! -s "${TMP_DIR}/${__sc_name}" ] && continue
                          _opwd="$( pwd )"
                          cd "${TMP_DIR}"
                          touch "${__sc_logfile}"
                          dialog ${DIALOG_OPTS} \
                              --backtitle "$( _backtitle "${_b_title}" )" \
                              --title " Running '${_app_menu_name}' script " \
                              --exit-label "...Please wait..." \
                              --tailbox \
                                  "${__sc_logfile}" \
                              "${max_lines}" "${max_columns}" &
                          RUN_LOG "echo $! > ${TMPFILE}"
                          RUN_LOG "bash './${__sc_name}' '${USERNAME}' '${USERPASSWORD}'"
                          __rc=$?
                          RUN_LOG "kill $( cat "${TMPFILE}" )"
                          [ "${CLEAN_TMP}" = "yes" ] \
                              && RM_FILES "${TMP_DIR}/${__sc_name}"
                          cd "${_opwd}"
                          [ ${__rc} -eq 0 -o ${__rc} -eq 112 ] \
                                  || continue
                          #
                          # Update menu database
                          #
                          sqlite3 "${MENU_DB}" \
                             "UPDATE application \
                              SET menu2='' \
                              WHERE id=${__id};"
                                >> "${LOGFILE}" 2>&1
                          sqlite3 "${MENU_DB}" \
                             "DELETE FROM application \
                              WHERE operation = 'uninstall' AND id=${__id};" \
                                >> "${LOGFILE}" 2>&1
                      done < <(sqlite3 "${APP_DB}" \
                                  "SELECT id FROM application \
                                   WHERE depends = '${app_name}' \
                                      OR depends LIKE '${app_name}|%' \
                                      OR depends LIKE '%|${app_name}|%' \
                                      OR depends LIKE '%|${app_name}';")
                      ;;

                  "upgrade")
                      if [ "${old_app_version}" != "${new_app_version}" ] ; then
                          echo "${app_name} ${old_app_version} upgraded to ${new_app_version}" \
                              >> "${OUTPUT}"
                      else
                          if [ "${app_name}" = "OS Update" ] ; then
                              echo "OS '${old_app_version}' has been updated." \
                                  >> "${OUTPUT}"
                          else
                              echo "${app_name} ${old_app_version} is already up to date!" \
                                  >> "${OUTPUT}"
                          fi
                      fi
                      sqlite3 "${MENU_DB}" \
                         "DELETE FROM application \
                          WHERE operation = '${op}' AND menu1 = '${app_menu_name}';" \
                            >> "${LOGFILE}" 2>&1
                      sqlite3 "${MENU_DB}" \
                         "UPDATE application \
                          SET menu2 = 'Installed (${new_app_version_short})' \
                          WHERE menu1 = '${app_menu_name}' AND operation = 'install';" \
                            >> "${LOGFILE}" 2>&1

                      ;;

                esac

            fi

            # Rebuild APP menu
            [ ${main_rc} -eq 0 ] \
                && _rebuild_menu

            time_end="$( date -u "+%s" )"
            time_spent="$(( time_end - time_start ))"
            str_time_spent="$( str_time_format "${time_spent}" )"
            if [ ${main_rc} -eq 0 ] ; then
                msg_time_spent="    Execution time    |\n\Zb\Zr\Z2${str_time_spent}\Zn"
            else
                msg_time_spent="    Execution time    |\n\Zb\Zr\Z1${str_time_spent}\Zn"
            fi
            display_output \
                     0 0 \
                     "  Results  " \
                     "${b_title}" \
                     "${msg_time_spent}"

            cd "${opwd}"

        done

    done


    main_rc=0
    continue

done

_exit_cls

) 9> "${LOCK_FILE}"
