#!/usr/bin/env bash


###
#
# Options Section
#
###

set -e
set -u
set -o pipefail


###
#
# Variable Section
#
###

REPOSITORY_URL='https://repository.privlab.it/archive/proxmox'
REPOSITORY_PATH='/var/lib/pct-templates'


###
#
# Function Section
#
###

function configuration () {
  local CONFIGURATION_REALPATH="${1}"
  local CONFIGURATION_BASENAME
  CONFIGURATION_BASENAME=$(/usr/bin/basename "${CONFIGURATION_REALPATH}")
  case "${CONFIGURATION_BASENAME}" in
    cleanup)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
## Cleanup Path Specification
##    Absolute path to the files or folders
##    which will be deleted from the template.
##
/dev/*
/lost+found/*
/media/*
/mnt/*
/proc/*
/run/*
/sys/*
/tmp/*
/tmp/.??*
/var/tmp/*
EOF
    ;;
    resolv.conf)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
# --- BEGIN PVE ---
# --- END PVE ---
EOF
    ;;
    .version)
      local DATE
      DATE=$(/usr/bin/date +%s)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
${DATE}
EOF
    ;;
  esac
}

function error () {
  if [[ -n "${1:-}" ]]; then
    /usr/bin/echo -n -e "${1}"
  fi
  if [[ -n "${2:-}" ]]; then
    /usr/bin/echo -n -e "${2}"
  fi
  exit 1
}

function consistency () {
  local CHROOT_PATH="${1}"
  /usr/sbin/chroot "${CHROOT_PATH}" /bin/sh -c 'exit 0'
}

function help () {
  local HELP="${1}"
  case "${HELP}" in
    archive)
      read -r -d '' HELP << EOL
Usage: pct-template -a (Template)

Description:
  Archives a Proxmox container template from the local repository,
  computes its checksum, and saves it to the current directory.

  Templates available in the local repository can be listed
  with the command 'pct-template -i'.
EOL
    ;;
    chroot)
      read -r -d '' HELP << EOL
Usage: pct-template -c (Image / Storage / Template)

Description:
  Opens a CHROOT environment for a Proxmox container template.
  The following host filesystems are binded and slaved into the CHROOT environment:
    - 'devtmpfs' : '/dev'
    - 'mqueue'   : '/dev/mqueue'
    - 'devpts'   : '/dev/pts'
    - 'proc'     : '/proc'
    - 'sys'      : '/sys'
    - 'tmpfs'    : '/run'
  All mounted filesystems are cleanly unmounted when exiting the CHROOT environment.
EOL
    ;;
    download)
      read -r -d '' HELP << EOL
Usage: pct-template -d (Template)

Description:
  Downloads a Proxmox container template from the online repository or updates it.
  If the local template is a newer version than the online repository,
  user interaction to delete the local template is required before overwriting.
  If the version in the online repository is newer, the local template is replaced
  by the version from the online repository.
EOL
    ;;
    generate)
      read -r -d '' HELP << EOL
Usage: pct-template -g (Image)
  Generate a Proxmox container template.
  It first checks whether the required template is available in the local repository;
  if not, it attempts to retrieve it from the online repository.
EOL
    ;;
    help)
      read -r -d '' HELP << EOL
Usage: pct-template (Option) (Image / Storage / Template)

Options:
  [ -a ] [ Template ]  |  Archive a Proxmox container template
  [ -c ] [ Template ]  |  Open a chroot environment in a Proxmxo container template
  [ -d ] [ Template ]  |  Dowload / Update a Proxmox container template to local repository
  [ -g ] [ Template ]  |  Generate a Promxox container template
  [ -h ]               |  Display help
  [ -i ]               |  Display repository information
  [ -m ] [ Template ]  |  Mount / Umount a Proxmox container template
  [ -u ] [ Template ]  |  Update an prepare Proxmox container template to a new version
EOL
    ;;
    info)
      read -r -d '' HELP << EOL
Usage: pct-template -i

Description:
  Lists all available Proxmox container templates from the local and
  online repositories and compares their versions.
EOL
    ;;
    mount)
      read -r -d '' HELP << EOL
Usage: pct-template -m (Image / Storage)

Description:
  Mount at path '/mnt' either a Proxmox container image or a storage filesystem;
  if the Proxmox container image or storage filesystem is already mounted, unmount it.
EOL
    ;;
    update)
      read -r -d '' HELP << EOL
Usage: pct-template -u (Template)

Description:
  An editor opens displaying a list of files and folders to be removed from the
  Proxmox container template. After removal, the template version is updated accordingly.
  Since this Proxmox container template is, in a broad sense, a fork, it should be
  considered whether renaming it would be appropriate.
EOL
    ;;
  esac
  /usr/bin/echo -e "${HELP}"
}

function template_download () {
  local TEMPLATE="${1}"
  local TEMPLATE_ONLINE_VERSION
  local RESTORE_DIRECTORY
  RESTORE_DIRECTORY=$(/usr/bin/pwd)
  output "Download Proxmox Container template '${TEMPLATE}' ..."
  TEMPLATE_ONLINE_VERSION=$(
    /usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
    /usr/bin/tr '\n' ' ' | \
    /usr/bin/sed --expression='s/</\n</g' | \
    /usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
    /usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
    /usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
    /usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
    /usr/bin/grep --invert-match '/$' | \
    /usr/bin/sed --expression='s/%20/ /g' | \
    /usr/bin/uniq | \
    /usr/bin/grep --extended-regexp "${TEMPLATE}\.[0-9]+\.tar\.xz$"
  ) || \
    error "\r\rDownload Proxmox container template '${TEMPLATE}' [ERROR]\n" \
          "  => The online repository is not reachable.\n"
  TEMPLATE_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${TEMPLATE_ONLINE_VERSION%.tar.xz}")
  /usr/bin/wget --quiet \
                --output-document="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" \
                --no-hsts "${REPOSITORY_URL}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" &> '/dev/null' || \
    error "\r\rDownload Proxmox container template '${TEMPLATE}' [ERROR]\n" \
          "  => The online repository is not reachable.\n"
  /usr/bin/wget --quiet \
                --output-document="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" \
                --no-hsts "${REPOSITORY_URL}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" &> '/dev/null' || \
    error "\r\rDownload Proxmox container template '${TEMPLATE}' [ERROR]\n" \
          "  => The online repository is not reachable.\n"
  output "\r\rDownload Proxmox container template '${TEMPLATE}' [OK]\n"
  output "Check the consistency of '${TEMPLATE}' ..."
  cd "${TMP}"
  /usr/bin/sha512sum --check \
                     --strict "${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" &> '/dev/null' || \
    error "\r\rCheck the consistency of '${TEMPLATE}' [ERROR]\n" \
          "  => The Proxmox container template '${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz' does not match the sha512 checksum.\n"
  cd "${RESTORE_DIRECTORY}"
  output "\r\rCheck the consistency of '${TEMPLATE}' [OK]\n"
  output "Decompress Proxmox container template '${TEMPLATE}' ..."
  /usr/bin/install --directory "${REPOSITORY_PATH}/${TEMPLATE}" || \
    error "\r\rDecompress Proxmox container template '${TEMPLATE}' [ERROR]\n" \
          "  => The Proxmox container template directory '${REPOSITORY_PATH}/${TEMPLATE}' could not be installed.\n"
  /usr/bin/tar --extract \
               --directory="${REPOSITORY_PATH}/${TEMPLATE}" \
               --file="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" || \
    error "\r\rDecompress Proxmox container template '${TEMPLATE}' [ERROR]\n" \
          "  => The Proxmox container template '${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz' could not be decompressed.\n"
  output "\r\rDecompress Proxmox container template '${TEMPLATE}' [OK]\n"
}

function mount_api () {
  local IMAGE_STORAGE_REALPATH="${1}"
  local IMAGE_STORAGE_BASENAME
  IMAGE_STORAGE_BASENAME=$(/usr/bin/basename "${IMAGE_STORAGE_REALPATH}")
  local MOUNTPOINT_PREFIX="/mnt/${IMAGE_STORAGE_BASENAME}"
  output "Mount 'devtmpfs' ..."
  /usr/bin/mount --bind '/dev' \
                 --target "${MOUNTPOINT_PREFIX}/dev" &> '/dev/null' || \
    error "\r\rMount 'devtmpfs' [ERROR]\n" \
          "  => The device 'devtmpfs' could not be mounted in '${MOUNTPOINT_PREFIX}/dev'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/dev" &> '/dev/null' || \
    error "\r\rMount 'devtmpfs' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev' could not be slaved.\n"
  output "\r\rMount 'devtmpfs' [OK]\n"
  output "Mount 'devpts' ..."
  /usr/bin/mount --bind '/dev/pts' \
                 --target "${MOUNTPOINT_PREFIX}/dev/pts" &> '/dev/null' || \
    error "\r\rMount 'devpts' [ERROR]\n" \
          "  => The device 'devpts' could not be mounted in '${MOUNTPOINT_PREFIX}/dev/pts'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/dev/pts" &> '/dev/null' || \
    error "\r\rSlave mountpoint 'devpts' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev/pts' could not be slaved.\n"
  output "\r\rMount 'devpts' [OK]\n"
  output "Mount 'mqueue' ..."
  /usr/bin/mount --bind '/dev/mqueue' \
                 --target "${MOUNTPOINT_PREFIX}/dev/mqueue" &> '/dev/null' || \
    error "\r\rMount 'mqueue' [ERROR]\n" \
          "  => The device 'mqueue' could not be mounted in '${MOUNTPOINT_PREFIX}/dev/mqueue'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/dev/mqueue" &> '/dev/null' || \
    error "\r\rMount 'mqueue' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev/mqueue' could not be slaved.\n"
  output "\r\rMount 'mqueue' [OK]\n"
  output "Mount 'proc' ..."
  /usr/bin/mount --types 'proc' \
                 --source '/proc' \
                 --target "${MOUNTPOINT_PREFIX}/proc" &> '/dev/null' || \
    error "\r\rMount 'proc' [ERROR]\n" \
          "  => The device 'proc' could not be mounted in '${MOUNTPOINT_PREFIX}/proc'.\n"
  output "\r\rMount 'proc' [OK]\n"
  output "Mount 'run' ..."
  /usr/bin/mount --bind '/run' --target "${MOUNTPOINT_PREFIX}/run" || \
    error "\r\rMount 'run' [ERROR]\n" \
          "  => The device 'sysfs' could not be mounted in '${MOUNTPOINT_PREFIX}/run'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/run" &> '/dev/null' || \
    error "\r\rSlave mountpoint 'tmpfs' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev/pts' could not be slaved.\n"
  output "\r\rMount 'run' [OK]\n"
  output "Mount 'sys' ..."
  /usr/bin/mount --types 'sysfs' \
                 --source '/sys' \
                 --target "${MOUNTPOINT_PREFIX}/sys" &> '/dev/null' || \
    error "\r\rMount 'sys' [ERROR]\n" \
          "  => The device 'sysfs' could not be mounted in '${MOUNTPOINT_PREFIX}/sys'.\n"
  output "\r\rMount 'sys' [OK]\n"
}

function mount_template () {
  local TEMPLATE_REALPATH="${1}"
  local TEMPLATE_BASENAME
  TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
  output "Prepare mount point for '${TEMPLATE_BASENAME}' ..."
  /usr/bin/install --directory "/mnt/${TEMPLATE_BASENAME}" || \
    error "\r\rPrepare mount point for '${TEMPLATE_BASENAME}' [ERROR]\n" \
          "  => The mount point '/mnt/${TEMPLATE_BASENAME}' could not be installed.\n"
  output "\r\rPrepare mount point for '${TEMPLATE_BASENAME}' [OK]\n"
  output "Mount '${TEMPLATE_BASENAME}' ..."
  /usr/bin/mount --bind "${TEMPLATE_REALPATH}" \
                 --target "/mnt/${TEMPLATE_BASENAME}" || \
    error "\r\rMount '${TEMPLATE_BASENAME}' [ERROR]\n" \
          "  => The template '${TEMPLATE_BASENAME}' could not be mounted in '/mnt/${TEMPLATE_BASENAME}'.\n"
  output "\r\rMount '${TEMPLATE_BASENAME}' [OK]\n"
}

function output () {
  if [[ -n "${1:-}" ]]; then
    /usr/bin/echo -n -e "${1}"
  fi
}

function table_padding () {
  local WIDTH="${1}"
  shift
  local ELLIPSIS="${1:-}"
  /usr/bin/printf "%-*s" "${WIDTH}" "${ELLIPSIS}"
}

function umount_template () {
  local TEMPLATE_REALPATH
  local TEMPLATE_BASENAME
  TEMPLATE_REALPATH="${1}"
  TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
  output "Unmount '${TEMPLATE_BASENAME}' ..."
  /usr/bin/umount --recursive "/mnt/${TEMPLATE_BASENAME}" || \
    error "\r\rUnmount '${TEMPLATE_BASENAME}' [ERROR]\n" \
          "  => The template '${TEMPLATE_BASENAME}' could not be unmounted from '/mnt/${TEMPLATE_BASENAME}'\n"
  output "\r\rUnmount '${TEMPLATE_BASENAME}' [OK]\n"
  if [[ -d "/mnt/${TEMPLATE_BASENAME}" ]]; then
    output "Remove mountpoint '/mnt/${TEMPLATE_BASENAME}' ..."
    /usr/bin/rm --force \
                --dir "/mnt/${TEMPLATE_BASENAME}" || \
      error "\r\rRemove mountpoint '/mnt/${TEMPLATE_BASENAME}' [ERROR]\n" \
            "  => The mountpoint '/mnt/${TEMPLATE_BASENAME}' could not be removed.\n"
    output "\r\rRemove mountpoint '/mnt/${TEMPLATE_BASENAME}' [OK]\n"
  fi
}


###
#
# Runtime Environment
#
###

/usr/bin/echo -e 'Proxmox Container Template Tool\n'

while getopts ':a:c:d:g:him:u:' OPT; do
  case "${OPT}" in
    a)
      INPUT="${OPTARG}"
      TEMPLATE_REALPATH="${REPOSITORY_PATH}/${INPUT}"
      TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}") || \
        error "\r\rArchive template '${INPUT}' [ERROR]\n" \
              "  => A Proxmox container template with the name '${INPUT}' does not exists.\n"
      /usr/bin/test -f "${REPOSITORY_PATH}/${TEMPLATE_BASENAME}/.version" && \
        TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${REPOSITORY_PATH}/${TEMPLATE_BASENAME}/.version")
      consistency "${TEMPLATE_REALPATH}"
      output "Archive template '${TEMPLATE_BASENAME}' ..."
      /usr/bin/tar --create \
                   --use-compress-program="/usr/bin/xz -9 --extreme" \
                   --file "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz" \
                   --directory="${TEMPLATE_REALPATH}" '.' || \
        error "\r\rArchive template '${TEMPLATE_BASENAME}' [ERROR]\n" \
              "  => The template '${TEMPLATE_BASENAME}' could not be archived.\n"
      output "\r\rArchive template '${TEMPLATE_BASENAME}' [OK]\n"
      output "Generate hashsum ..."
      /usr/bin/sha512sum "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz" | \
      /usr/bin/sed --expression='s, .*/,  ,' >> "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz.sha512sum" || \
        error '\r\rGenerate hashsum [ERROR]\n' \
              '  => The hashsum for the archived template could not be generated.\n'
      output '\r\rGenerate hashsum [OK]\n'
      exit 0
    ;;
    c)
      INPUT="${OPTARG}"
      if [[ -d "${REPOSITORY_PATH}/${INPUT}" ]] && \
         [[ ! -d "./${INPUT}" ]]; then
        INPUT_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
      else
        INPUT_REALPATH=$(/usr/bin/realpath "${INPUT}")
      fi
      consistency "${INPUT_REALPATH}"
      TEMPLATE_REALPATH="${INPUT_REALPATH}"
      TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
      if /usr/bin/grep --word-regexp --quiet "${TEMPLATE_BASENAME}" '/proc/mounts' ; then
        TEMPLATE_MOUNTED="1"
      fi
      if [[ -z "${TEMPLATE_MOUNTED:-}" ]]; then
        mount_template "${TEMPLATE_REALPATH}"
        mount_api "${TEMPLATE_REALPATH}"
        output '\n ... Open chroot environment ... \n\n'
        /usr/bin/ln --force --symbolic "/run/systemd/resolve/stub-resolv.conf" "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
          error "\r\r... Open chroot environment ... [ERROR]\n" \
                "  => The 'resolv.conf' could not be prepared.\n"
        /usr/bin/sleep '3s'
        /usr/sbin/chroot "/mnt/${TEMPLATE_BASENAME}" || \
          /usr/bin/true
        output '\n ... Close chroot environment ... \n\n'
        /usr/bin/unlink "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
          error "\r\r... Open chroot environment ... [ERROR]\n" \
                "  => The 'resolv.conf' could not be unlinked.\n"
        configuration "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
          error "\r\r... Open chroot environment ... [ERROR]\n" \
                "  => The 'resolv.conf' could not be prepared.\n"
        umount_template "${TEMPLATE_REALPATH}"
      elif [[ -n "${TEMPLATE_MOUNTED}" ]]; then
        for PID in $(/usr/bin/ps -e -o 'pid='); do
          if [[ -e "/proc/${PID}/root" ]] && \
             [[ "$(/usr/bin/readlink --canonicalize /proc/${PID}/root)" == "/mnt/${TEMPLATE_BASENAME}" ]]; then
            error "\r\rOpen chroot environment for '${TEMPLATE_BASENAME}' [ERROR]\n" \
                  "  => There is already a chroot envrionment for the Proxmox container template '${TEMPLATE_BASENAME}' opened.\n"
          fi
        done
        umount_template "${TEMPLATE_REALPATH}"
        mount_template "${TEMPLATE_REALPATH}"
        mount_api "${TEMPLATE_REALPATH}"
        output '\n ... Open chroot environment ... \n\n'
        /usr/bin/ln --force --symbolic "/run/systemd/resolve/stub-resolv.conf" "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
          error "\r\r... Open chroot environment ... [ERROR]\n" \
                "  => The 'resolv.conf' could not be prepared.\n"
        /usr/bin/sleep '3s'
        /usr/sbin/chroot "/mnt/${TEMPLATE_BASENAME}" || \
          /usr/bin/true
        output '\n ... Close chroot environment ... \n\n'
        /usr/bin/unlink "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
          error "\r\r... Open chroot environment ... [ERROR]\n" \
                "  => The 'resolv.conf' could not be unlinked.\n"
        configuration "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
          error "\r\r... Open chroot environment ... [ERROR]\n" \
                "  => The 'resolv.conf' could not be prepared.\n"
        umount_template "${TEMPLATE_REALPATH}"
      fi
      exit 0
    ;;
    d)
      INPUT="${OPTARG}"
      TMP=$(/usr/bin/mktemp --directory --quiet)
      trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
      TEMPLATE_BASENAME="${INPUT}"
      TEMPLATE_REALPATH="${REPOSITORY_PATH}/${TEMPLATE_BASENAME}"
      TEMPLATE_ONLINE_VERSION=$(
        /usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
        /usr/bin/tr '\n' ' ' | \
        /usr/bin/sed --expression='s/</\n</g' | \
        /usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
        /usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
        /usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
        /usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
        /usr/bin/grep --invert-match '/$' | \
        /usr/bin/sed --expression='s/%20/ /g' | \
        /usr/bin/uniq | \
        /usr/bin/grep --extended-regexp "${TEMPLATE_BASENAME}\.[0-9]+\.tar\.xz$"
      )
      TEMPLATE_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${TEMPLATE_ONLINE_VERSION%.tar.xz}")
      /usr/bin/test -f "${TEMPLATE_REALPATH}/.version" && \
        TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${TEMPLATE_REALPATH}/.version")
      if [[ ! -d "${TEMPLATE_REALPATH}" ]]; then
        template_download "${TEMPLATE_BASENAME}"
      elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
           [[ "${TEMPLATE_LOCAL_VERSION}" -lt "${TEMPLATE_ONLINE_VERSION}" ]]; then
        output "Remove Proxmox container template '${TEMPLATE_BASENAME}' ..."
        /usr/bin/rm --force --recursive "${TEMPLATE_REALPATH}" || \
          error "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
                "  => The Proxmox container template archive under '${TEMPLATE_REALPATH}' could not be removed.\n"
        output "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [OK]\n"
        template_download "${TEMPLATE_BASENAME}"
      elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
           [[ "${TEMPLATE_LOCAL_VERSION}" -gt "${TEMPLATE_ONLINE_VERSION}" ]]; then
        read -r -p $'The Proxmox container template in the local repository is newer\nthan the template in the online repository.\n\nWould you like to overwrite the local template? [Yes/No] ' ANSWER
        if [[ "${ANSWER,,}" == "yes" ]]; then
          output "Remove Proxmox container template '${TEMPLATE_BASENAME}' ..."
          /usr/bin/rm --force --recursive "${TEMPLATE_REALPATH}" || \
            error "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
                  "  => The Proxmox container template under '${TEMPLATE_REALPATH}' could not be removed.\n"
          output "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [OK]\n"
          template_download "${TEMPLATE_BASENAME}"
        else
          exit 0
        fi
      elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
           [[ "${TEMPLATE_LOCAL_VERSION}" -eq "${TEMPLATE_ONLINE_VERSION}" ]]; then
            error "The Proxmox container template '${TEMPLATE_BASENAME}' is already up to date.\n"
      fi
      exit 0
    ;;
    g)
      INPUT="${OPTARG}"
      TMP=$(/usr/bin/mktemp --directory --quiet)
      trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
      TEMPLATE_BASENAME="${OPTARG}"
      TEMPLATE_REALPATH="${REPOSITORY_PATH}/${TEMPLATE_BASENAME}"
      if [[ ! -d "${TEMPLATE_REALPATH}" ]]; then
        template_download "${TEMPLATE_BASENAME}"
      fi
      /usr/bin/test -f "./${TEMPLATE_BASENAME}.tar.xz" && \
        error '\r\rGenerate Proxmox container image [ERROR]\n' \
              "  => A Proxmox container template with the name '${TEMPLATE_BASENAME}' already exists.\n"
      consistency "${TEMPLATE_REALPATH}"
      output "Generate Proxmox container template '${TEMPLATE_BASENAME}' ..."
      /usr/bin/tar --create \
                   --use-compress-program="/usr/bin/xz -9 --extreme" \
                   --exclude='.cleanup' \
                   --exclude='.version' \
                   --file "./${TEMPLATE_BASENAME}.tar.xz" \
                   --directory="${TEMPLATE_REALPATH}" '.' || \
        error "\r\rGenerate Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
              "  => The Proxmox container template '${TEMPLATE_BASENAME}' could not be generated.\n"
      output "\r\rGenerate Proxmox container template '${TEMPLATE_BASENAME}' [OK]\n"
      output "Generate Proxmox container template hashsum ..."
      /usr/bin/sha512sum "./${TEMPLATE_BASENAME}.tar.xz" | \
      /usr/bin/sed --expression='s, .*/,  ,' >> "./${TEMPLATE_BASENAME}.tar.xz.sha512sum" || \
        error '\r\rGenerate Proxmox container template hashsum [ERROR]\n' \
              '  => The hashsum for the Proxmox container template could not be generated.\n'
      output '\r\rGenerate Proxmox container template hashsum [OK]\n'
      exit 0
    ;;
    i)
      if [[ -d "${REPOSITORY_PATH}" ]]; then
        mapfile -t REPOSITORY_LOCAL_FILES < <(
          /usr/bin/find "${REPOSITORY_PATH}/" -mindepth '1' -maxdepth '1' -type 'd' ! -name 'lost+found' -exec /usr/bin/basename {} \;
        )
      fi
      mapfile -t REPOSITORY_ONLINE_FILES < <(
        /usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
        /usr/bin/tr '\n' ' ' | \
        /usr/bin/sed --expression='s/</\n</g' | \
        /usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
        /usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
        /usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
        /usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
        /usr/bin/grep --invert-match '/$' | \
        /usr/bin/sed --expression='s/%20/ /g' | \
        /usr/bin/uniq | \
        /usr/bin/grep --extended-regexp '\.tar\.xz$'
      )
      REPOSITORY_FILES_PIPE=("${REPOSITORY_LOCAL_FILES[@]}" "${REPOSITORY_ONLINE_FILES[@]}")
      mapfile -t REPOSITORY_FILES_PIPE < <(
        /usr/bin/printf '%s\n' "${REPOSITORY_FILES_PIPE[@]}" | \
        /usr/bin/sort --key='1,1' \
                      --key='2,2r' \
                      --field-separator='.'
      )
      for REPOSITORY_FILE in "${REPOSITORY_FILES_PIPE[@]}"; do
        REPOSITORY_FILES+=("${REPOSITORY_FILE%.tar.xz}")
      done
      /usr/bin/printf ' '
      /usr/bin/printf '%.0s-' {1..108}
      /usr/bin/printf "\n| %-40s | %-30s | %-30s | %s\n" "Proxmox container Template" "Verion (Local Repository)" "Version (Online Repository)"
      /usr/bin/printf ' '
      /usr/bin/printf "%.0s-" {1..108}
      /usr/bin/printf "\n"
      for REPOSITORY_FILE in "${REPOSITORY_FILES[@]}"; do
        REPOSITORY_TEMPLATE="${REPOSITORY_FILE//.[0-9]*/}"
        for REPOSITORY_TEMPLATE_DIFF in "${REPOSITORY_LOOP[@]:-}"; do
          if [[ "$REPOSITORY_TEMPLATE" == "$REPOSITORY_TEMPLATE_DIFF" ]]; then
            REPOSITORY_LOOP_BREAK='1'
            break
          fi
        done
        if [[ "${REPOSITORY_LOOP_BREAK:-}" -eq '1' ]]; then
          unset REPOSITORY_LOOP_BREAK
          continue
        fi
        REPOSITORY_LOOP+=("${REPOSITORY_TEMPLATE}")
        if [[ "${REPOSITORY_FILE}" =~ \.([0-9]+) ]]; then
          REPOSITORY_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${REPOSITORY_FILE}")
        fi
        if [[ -d "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}" ]] && \
           [[ -s "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version" ]]; then
          REPOSITORY_LOCAL_VERSION=$(/usr/bin/cat "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version")
        elif [[ -d "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}" ]] && \
             [[ ! -s "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version" ]]; then
          REPOSITORY_LOCAL_VERSION='N/V'
        fi
        REPOSITORY_TEMPLATE_PADDING=$(table_padding '40' "${REPOSITORY_TEMPLATE:-}")
        REPOSITORY_LOCAL_VERSION_PADDING=$(table_padding '30' "${REPOSITORY_LOCAL_VERSION=N/A}")
        REPOSITORY_ONLINE_VERSION_PADDING=$(table_padding '30' "${REPOSITORY_ONLINE_VERSION:=N/A}")
        REPOSITORY_TABLE_FORMAT_DIM=$'\e[2m'
        REPOSITORY_TABLE_FORMAT_GREEN=$'\e[92m'
        REPOSITORY_TABLE_FORMAT_RED=$'\e[91m'
        REPOSITORY_TABLE_FORMAT_RESET=$'\e[0m'
        if [[ -n "${REPOSITORY_LOCAL_VERSION:-}" ]] && \
           [[ -n "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
          if [[ "${REPOSITORY_LOCAL_VERSION:-}" -lt "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
            REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_RED}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
            REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_GREEN}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
          elif [[ "${REPOSITORY_LOCAL_VERSION:-}" -gt "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
            REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_GREEN}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
            REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_RED}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
          fi
        fi
        if [[ "${REPOSITORY_LOCAL_VERSION:-}" == 'N/V' ]] || \
           [[  "${REPOSITORY_LOCAL_VERSION:-}" == 'N/A' ]] || \
           [[ -z "${REPOSITORY_LOCAL_VERSION:-}" ]]; then
          REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_DIM}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
        fi
        if [[ -z "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
          REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_DIM}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
        fi
        /usr/bin/printf '| %s | %s | %s | %s\n' \
                        "${REPOSITORY_TEMPLATE_PADDING:-}" \
                        "${REPOSITORY_LOCAL_VERSION_PADDING:-}" \
                        "${REPOSITORY_ONLINE_VERSION_PADDING:-}"
        unset REPOSITORY_ONLINE_VERSION
        unset REPOSITORY_LOCAL_VERSION
      done
      /usr/bin/printf ' '
      /usr/bin/printf "%.0s-" {1..108}
      /usr/bin/printf "\n"
      exit 0
    ;;
    m)
      INPUT="${OPTARG}"
      if [[ -d "${REPOSITORY_PATH}/${INPUT}" ]] && \
         [[ ! -d "./${INPUT}" ]]; then
        INPUT_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
      else
        INPUT_REALPATH=$(/usr/bin/realpath "${INPUT}")
      fi
      consistency "${INPUT_REALPATH}"
      TEMPLATE_REALPATH="${INPUT_REALPATH}"
      TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
      if /usr/bin/grep --quiet "${TEMPLATE_BASENAME}" '/proc/mounts'; then
        TEMPLATE_MOUNTED="1"
      fi
      if [[ -z "${TEMPLATE_MOUNTED:-}" ]]; then
        mount_template "${TEMPLATE_REALPATH}"
      elif [[ -n "${TEMPLATE_MOUNTED}" ]]; then
        umount_template "${TEMPLATE_REALPATH}"
      fi
      exit 0
    ;;
    u)
      INPUT="${OPTARG}"
      TEMPLATE_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
      TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
      TMP=$(/usr/bin/mktemp --directory --quiet)
      trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
      /usr/bin/grep --quiet "${TEMPLATE_REALPATH}" '/proc/mounts' && \
        error "\r\rUpdate version of Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
              "  => The Proxmox container template '${TEMPLATE_BASENAME}' is mounted.\n"
      consistency "${TEMPLATE_REALPATH}"
      output 'Load configuration for the Proxmox container template cleanup ...'
      if [[ -f "${TEMPLATE_REALPATH}/.cleanup" ]]; then
        /usr/bin/cp --dereference "${TEMPLATE_REALPATH}/.cleanup" "${TMP}/cleanup" || \
          error '\r\rLoad configuration for the Proxmox container template cleanup [ERROR]\n' \
                "  => The configuration template '{TEMPLATE_REALPATH}/.cleanup' could not be copied to '{TMP}/cleanup'.\n"
      else
        configuration "${TMP}/cleanup" || \
          error '\r\rLoad configuration for the Proxmox container template cleanup [ERROR]\n' \
                "  => The default configuration template could not be copied to '{TMP}/cleanup'.\n"
      fi
      output '\r\rLoad configuration for the Proxmox container template cleanup [OK]\n'
      output 'Remove configured files and folders ...'
      mapfile -t TEMPLATE_REMOVE_LIST < <(
        /usr/bin/sed '/#.*/d' < "${TMP}/cleanup" | \
        while read -r TEMPLATE_REMOVE_PATH; do \
          /usr/bin/echo "${TEMPLATE_REALPATH}${TEMPLATE_REMOVE_PATH}"; \
        done
      )
      for X in "${TEMPLATE_REMOVE_LIST[@]}"; do
        /usr/bin/sh -c "/usr/bin/ls --directory ${X} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
          error '\r\rRemove configured files and folders [ERROR]\n' \
                "  => The file or directory '${X}' could not be removed.\n"
      done
      output '\r\rRemove configured files and folders [OK]\n'
      TEMPLATE_NEW_VERSION=$(/usr/bin/date +%s)
      output "Update Proxmox container template verion to '${TEMPLATE_NEW_VERSION}' ..."
      configuration "${TEMPLATE_REALPATH}/.version" || \
        error "\r\rUpdate Proxmox container template verion to '${TEMPLATE_NEW_VERSION}' [ERROR]\n" \
              "  => The template version cloud not be updated.\n"
      output "\r\rUpdate Proxmox container template verion to '${TEMPLATE_NEW_VERSION}' [OK]\n"
      exit 0
    ;;
    -*|*)
      set +e
      case "${1}" in
        -a)
          help 'archive'
          exit 0
        ;;
        -c)
          help 'chroot'
          exit 0
        ;;
        -d)
          help 'download'
          exit 0
        ;;
        -g)
          help 'generate'
          exit 0
        ;;
        -i)
          help 'info'
          exit 0
        ;;
        -m)
          help 'mount'
          exit 0
        ;;
        -u)
          help 'update'
          exit 0
        ;;
      esac
    ;;
  esac
done
set +e
help 'help'
shift $((OPTIND-1))
