#!/bin/sh

#
# backup ab-openLDAP configuration and frontend database(s)
# version 6.0.0
# Script by Asif Bacchus
#

### text formatting presets

# set colours for various log message types
bold=$(tput bold)
err=$(tput bold)$(tput setaf 1)
info=$(tput sgr0)
norm=$(tput sgr0)
ok=$(tput setaf 2)

# define other colours used
cyan=$(tput bold)$(tput setaf 6)
magenta=$(tput sgr0)$(tput setaf 5)
yellow=$(tput sgr0)$(tput setaf 3)

### parameter defaults
myVersion='6.0.0'
width=$(tput cols)
scriptPath="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)"
scriptName="$(basename "$0")"
# logfile default: same location and name as this script, with '.log' extension
logfile="$scriptPath/${scriptName%.*}.log"
# encryption parameters file default: same location and name as this script,
# with '.params' extension
encParams="$scriptPath/${scriptName%.*}.params"
# backup mode by default
unset removeTLS
unset backupFile
extract=false
decrypt=false
# encryption enabled by default
noEncryption=0
tempDir="$(date '+%s')"
fileDate="$(date '+%F_%T')"
outputLocation="$scriptPath/"
# frontend starts at '1', so this number should always be the number of
# frontend databases + 1 -- default to only 1 frontend db
numFrontEnd=2
# delete backups older than 7 days by default
deleteBackupsOlderThan=7

### functions

cleanup() {
  logInfo 'task' 'Cleaning up'
  if ! docker exec "$container" rm -rf "$tempDir" \
    >>"$logfile" 2>&1; then
    logInfo 'err'
    exitError 3 'Unable to remove temporary files in container.' 'nc'
  fi
  logInfo 'done'
}

consoleError() {
  printf "%s\n%s\n" "$err" "$2"
  printf "Exiting.\n\n%s" "$norm"
  exit "$1"
}

decryptionNote() {
  printf "\n"
  textBlock "${bold}${magenta}Decrypting your backup archive:${norm}"
  printf "\n"

  textBlock 'To decrypt and extract your backup file, you *must* know the password used to encrypt the file.'
  textBlock 'If you have been using this script to make your backups, the password is stored in the backup parameters file (default: "ab-openldap-backup.params").'
  printf "\n"
  textBlock "Run the following command, replacing the ${cyan}[cyan stuff in square brackets]${norm} with values appropriate to your environment."
  printf "\n"
  textBlock "gpg --pinentry-mode loopback --passphrase \"${cyan}[password]${norm}\" -a --decrypt ${cyan}[/path/to/backup_archive.tar.gz]${norm} | tar --overwrite -xz -C ${cyan}[destinationDirectory]${norm}"
  printf "\n"
  textBlock 'NOTE: The output directory must already exist or tar will throw an error.'
  printf "\n"
  textBlock "${magenta}Example:${norm}"
  textBlock "gpg --pinentry-mode loopback --passphrase \"myPassword\" -a --decrypt ~/ldap-2024-03-06_17:30:00.tar.gz | tar --overwrite -xz -C ~/ldapRestore/"
  printf "\n"
  textBlock 'Note: Using this script with the --decrypt option handles this for you and can create target directories automatically.'
  printf "\n\n"
  exit 0
}

exitError() {
  # cleanup temp directory unless 'nc' passed
  if [ ! "$3" = "nc" ]; then cleanup; fi

  # log error
  printf "%s[%s] -- [ERROR] code %s: %s --%s\n" \
    "$err" "$(stamp)" "$1" "$2" "$norm" >>"$logfile"
  printf "%s[%s] --- %s terminated with errors ---\n%s" \
    "$err" "$(stamp)" "$scriptName" "$norm" >>"$logfile"

  # exit with proper error code
  exit "$1"
}

logInfo() {
  if [ "$1" = 'task' ]; then
    printf "%s[%s] -- [INFO] %s... " \
      "$info" "$(stamp)" "$2" >>"$logfile"
  elif [ "$1" = 'done' ]; then
    if [ -z "$2" ]; then
      printf "%sDONE%s --%s\n" \
        "$ok" "$info" "$norm" >>"$logfile"
    else
      printf "%s%s%s --%s\n" \
        "$ok" "$2" "$info" "$norm" >>"$logfile"
    fi
  elif [ "$1" = 'err' ]; then
    printf "%sERROR%s --%s\n" \
      "$err" "$info" "$norm" >>"$logfile"
  else
    printf "%s[%s] -- [INFO] %s --%s\n" \
      "$info" "$(stamp)" "$1" "$norm" >>"$logfile"
  fi
}

logSuccess() {
  printf "%s[%s] -- [SUCCESS] %s --%s\n" \
    "$ok" "$(stamp)" "$1" "$norm" >>"$logfile"
}

scriptHelp() {
  printf "\n"
  textBlock "${bold}${magenta}Usage: ${scriptName} [parameters]${norm}"
  printf "\n"
  textBlock "${cyan}Parameters ${yellow}(default value):${norm}"
  textBlock "There are NO mandatory parameters. By default the script will run in 'backup' mode, save an encrypted backup archive to the current directory and delete any backups older than 7 days. If a parameter is not supplied, its default value will be used. In the case of a switch parameter, it will remain deactivated if not specified."
  printf "\n"
  textBlock "${bold}*** Common parameters ***${norm}"
  printf "\n"
  textBlock "${cyan}-l, --log ${yellow}(scriptPath/scriptName.log)${norm}"
  textBlock "FULL path to write log file. If you supply a path ending with a slash ('/') it will be assumed you mean a directory and the log file will be written to that directory using the format 'path/scriptName.log'. If you supply only a filename (no slashes anywhere), it will be assumed you want to save the log using that name in the script directory. The script will attempt to create any provided paths/directories if they do not exist."
  printf "\n"
  textBlock "${cyan}-o, --output ${yellow}(scriptPath/)${norm}"
  textBlock "Location where the output files should be saved on this machine. You should only specify a *directory* here (trailing slash optional). File names are automatic and cannot be changed via this script. All restore operations will create a 'restore' subdirectory in this specified directory."
  printf "\n"
  textBlock "${cyan}-p, --params ${yellow}(scriptPath/scriptName.params)${norm}"
  textBlock "Location of the encryption parameters file. This file contains the password and encryption options that should be used. By default, the script looks in the script directory for a file named the same as the script but with a '.params' extension."
  printf "\n"
  textBlock "${cyan}--delete-older-than ${yellow}(7)${norm}"
  textBlock "Delete backup files in output directory older than the specified number of days. Setting this to 0 will disable deleting old backups."
  printf "\n"
  textBlock "${cyan}-v, --version${norm}"
  textBlock "Display the version of this script and exit."
  printf "\n"
  textBlock "${cyan}-h, -? | -??, --help${norm}"
  textBlock "Quick-help screen. | This (detailed) help screen."
  printf "\n"
  textBlock "${bold}*** Encryption parameters ***${norm}"
  printf "\n"
  textBlock "${cyan}-n, --num, --frontend ${yellow}(1)${norm}"
  textBlock "Number of frontend databases to backup. If you specify a number here greater than actually exist, openLDAP will generate an error and your backup will fail."
  printf "\n"
  textBlock "${cyan}--no-encryption${norm}"
  textBlock "Switch parameter. Specify this if you DO NOT want the backup archive tar.gz file encrypted. Careful!"
  printf "\n"
  textBlock "${bold}*** Decryption/Extraction parameters ***${norm}"
  printf "\n"
  textBlock "${cyan}-b, --backupfile${norm}"
  textBlock "FULL path of the backup file you want to decrypt/extract. MUST be supplied if using the --decrypt or --extract switches."
  printf "\n"
  textBlock "${cyan}-d, --decrypt ${yellow}(implies --extract)${norm}"
  textBlock "Switch parameter. Decrypt and extract your backup file (MUST be supplied using the --backupfile parameter) to the 'restore' subdirectory of the current directory or as specified using the '--output' parameter. This switch will log error code 8 if you try to decrypt an unencrypted backup archive."
  printf "\n"
  textBlock "${cyan}-e, --extract${norm}"
  textBlock "Switch parameter. Extract your backup file (MUST be supplied using the --backupfile parameter) to the 'restore' subdirectory of the current directory or as specified using the '--output' parameter. This switch will log error code 8 if you try to extract an encrypted backup archive. In that case, use --decrypt instead."
  printf "\n"
  textBlock "${cyan}--removeTLS${norm}"
  textBlock "Switch parameter. Remove olcTLS configuration parameters from your config DIT LDIF file. This is the only approved way to disable/change your TLS settings. Consult the openLDAP documentation or the repo wiki for more information."
  printf "\n"
  textBlock "${cyan}--manualdecryption${norm}"
  textBlock "Display instructions on how to manually decrypt and extract your backup archive."
  printf "\n\n"
  textBlock "More details and examples of script usage can be found in the repo wiki at ${magenta}https://git.asifbacchus.dev/ab-docker/openldap/wiki${norm}"
  printf "\n"
}

stamp() {
  (date +%F' '%T)
}

textBlock() {
  printf "%s\n" "$1" | fold -w "$width" -s
}

quickHelp() {
  textBlock "${bold}${magenta}Usage: ${scriptName} [parameters]${norm}"
  printf "\n"
  textBlock "${cyan}Parameters ${yellow}(default value):${norm}"
  printf "\n"
  textBlock "${bold}*** Common parameters ***${norm}"
  textBlock "${cyan}-l, --log ${yellow}(scriptPath/scriptName.log)${norm}"
  textBlock "FULL path to write log file. Path ending with '/' will be treated as a directory. Filename only will be saved in script directory."
  textBlock "${cyan}-o, --output ${yellow}(scriptPath/)${norm}"
  textBlock "Directory to save output files on this machine."
  textBlock "${cyan}-p, --params ${yellow}(scriptPath/scriptName.params)${norm}"
  textBlock "Location of the encryption parameters file."
  textBlock "${cyan}--delete-older-than ${yellow}(7)${norm}"
  textBlock "Delete backups in output directory older than specified number of days. 0 = disabled."
  textBlock "${cyan}-v, --version${norm}"
  textBlock "Display the version of this script and exit."
  textBlock "${cyan}-h, -? | -??, --help${norm}"
  textBlock "This help screen. | Detailed help."
  printf "\n"
  textBlock "${bold}*** Encryption parameters ***${norm}"
  textBlock "${cyan}-n, --num, --frontend ${yellow}(1)${norm}"
  textBlock "Number of frontend databases to backup."
  textBlock "${cyan}--no-encryption${norm}"
  textBlock "Switch: DO NOT encrypt backup tar.gz."
  printf "\n"
  textBlock "${bold}*** Decryption/Extraction parameters ***${norm}"
  textBlock "${cyan}-b, --backupfile${norm}"
  textBlock "FULL path of the backup file to be decrypted/extracted."
  textBlock "${cyan}-d, --decrypt ${yellow}(implies --extract)${norm}"
  textBlock "Decrypt and extract specified backup file to 'restore' subdirectory of current path/specified path (--output parameter)."
  textBlock "${cyan}-e, --extract${norm}"
  textBlock "Extract specified backup file to 'restore' subdirectory of current path/specified path (--output parameter)."
  textBlock "${cyan}--removeTLS${norm}"
  textBlock "Remove all olcTLS configuration settings from your config DIT LDIF restore file."
  textBlock "${cyan}--manualdecryption${norm}"
  textBlock "Display instructions on manually decrypting and extracting your backup archive."
  printf "\n"
  textBlock "Run script with '-?? or --help' for detailed help. More details and examples of script usage can be found in the repo wiki at:"
  printf "%s\thttps://git.asifbacchus.dev/ab-docker/openldap/wiki%s\n\n" \
    "$magenta" "$norm"
}

### pre-requisite checks

# is user root or in the docker group?
if [ ! "$(id -u)" -eq 0 ]; then
  if ! id -Gn | grep docker >/dev/null; then
    consoleError '1' "You must either be root or in the 'docker' group to run this script since you must be able to run 'docker exec' commands against the container!"
  fi
fi

# is tar installed?
if ! command -v tar >/dev/null; then
  consoleError '99' 'It appears that tar is not installed. This script requires tar in order to package and process backup files.'
fi

# is gzip installed?
if ! command -v gzip >/dev/null; then
  consoleError '99' 'It appears that gzip is not installed. This script requires gzip in order to compress/decompress backup tarball files.'
fi

# process startup parameters
while [ $# -gt 0 ]; do
  case "$1" in
  -b | --backupfile)
    # name of backup file to decrypt/extract
    if [ -z "$2" ]; then
      consoleError '1' 'Backup file name cannot be null.'
    elif [ ! -f "$2" ]; then
      consoleError '1' "Specified backup file does not exist ($2)."
    fi
    backupFile="$2"
    shift
    ;;
  -d | --decrypt)
    # decrypt and extract backup file (implies '-e')
    decrypt=true
    extract=true
    ;;
  --delete-older-than)
    # delete backups older than this number of days
    if [ -z "$2" ]; then
      consoleError '1' 'Cannot specify a null number of days after which to delete old backups.'
    fi
    deleteBackupsOlderThan="$2"
    shift
    ;;
  -e | --extract)
    # extract gzipped tarball
    extract=true
    ;;
  -h | -\?)
    # display brief help
    quickHelp
    exit 0
    ;;
  -\?\? | --help)
    # display full help
    scriptHelp
    exit 0
    ;;
  -l | --log)
    # location of log file
    if [ -z "$2" ]; then
      consoleError '1' 'Log file path cannot be null. Leave unspecified to save log in the same directory as this script.'
    fi
    logfile="$2"
    shift
    ;;
  --manualdecryption)
    decryptionNote
    exit 0
    ;;
  -n | --num | --frontend)
    # number of frontend databases
    if [ -z "$2" ]; then
      consoleError '1' 'Number of frontend databases cannot be null.'
    fi
    numFrontEnd=$(($2 + 1))
    shift
    ;;
  --no-encryption)
    # disable encryption, switch parameter
    noEncryption=1
    # switches do not require an internal 'shift'
    ;;
  -o | --output)
    # directory on host to save output files
    if [ -z "$2" ]; then
      consoleError '1' 'Output destination location cannot be null.'
    fi
    outputLocation="$2"
    shift
    ;;
  -p | --params)
    # path to encryption parameters file
    if [ -z "$2" ]; then
      consoleError '1' 'Path to encryption parameters file cannot be null.'
    fi
    encParams="$2"
    shift
    ;;
  --removeTLS)
    # remove olcTLS entries from cn=config
    removeTLS=true
    ;;
  -v | --version)
    # display script version
    printf "\n%sBackup script version: %s%s\n" "$magenta" "$myVersion" "$norm"
    exit 0
    ;;
  *)
    printf "%s\nUnknown option: %s\n" "$err" "$1"
    printf "Use '--help' for valid options.\n\n%s" "$norm"
    exit 1
    ;;
  esac
  shift
done

### start logging
# logfile checks
if ! printf "%s" "$logfile" | grep -o / >/dev/null; then
  # filename provided, save in scriptDir
  logfile="$scriptPath/$logfile"
elif [ "$(printf "%s" "$logfile" | tail -c 1)" = '/' ]; then
  # directory provided, does it exist?
  if [ -d "$logfile" ]; then
    logfile="${logfile}${scriptName%.*}.log"
  else
    if ! mkdir -p "$logfile" >/dev/null 2>&1; then
      consoleError '1' 'Unable to make specified log file directory.'
    fi
    logfile="${logfile}${scriptName%.*}.log"
  fi
else
  # full path provided, does the parent directory exist?
  if [ ! -d "${logfile%/*}" ]; then
    # make parent path
    if ! mkdir -p "${logfile%/*}" >/dev/null 2>&1; then
      consoleError '1' 'Unable to make specified log file path.'
    fi
  fi
fi
# write initial log entries for script run
if ! printf "%s[%s] --- Start %s execution ---%s\n" \
  "$magenta" "$(stamp)" "$scriptName" "$norm" >>"$logfile"; then
  consoleError '1' "Unable to write to log file ($logfile)"
fi
logInfo "Log located at $logfile"

### process output directory
if [ -d "$outputLocation" ]; then
  # touch test to ensure we can write here
  if ! touch "${outputLocation%/}/test.touch" >/dev/null 2>&1; then
    exitError '1' "Unable to write to output location: $outputLocation" 'nc'
  else
    logInfo "Writing output to: $outputLocation"
    rm -f "${outputLocation%/}/test.touch" >/dev/null 2>&1
  fi
else
  # create directory
  if ! mkdir -p "${outputLocation%/}" >/dev/null 2>&1; then
    exitError '1' "Unable to create output path: $outputLocation" 'nc'
  else
    logInfo "Writing output to: $outputLocation"
  fi
fi

### source encryption parameters file

# check if file exists (unless encryption disabled), otherwise exit
if [ $noEncryption = 1 ]; then
  logInfo 'Backup archive will NOT be encrypted!'
elif [ $noEncryption = 0 ] && [ ! -f "$encParams" ]; then
  exitError 1 'Encryption parameters file does not exist.' 'nc'
else
  # import encryption parameters file
  case "${encParams}" in
  /*)
    # absolute path, no need to rewrite
    # shellcheck source=./ab-openldap-backup.params.template
    . "${encParams}"
    ;;
  *)
    # relative path, need to rewrite assuming current directory
    # shellcheck source=./ab-openldap-backup.params.template
    . "./${encParams}"
    ;;
  esac
  logInfo "Imported: '$encParams'"

  # verify import
  logInfo 'task' 'Verify encryption password'
  if [ -z "$password" ]; then
    logInfo 'err'
    exitError 1 "Imported null value for encryption 'password'." 'nc'
  else
    logInfo 'done'
  fi
fi

if [ $extract = 'true' ]; then
  # ensure backupFile has been specified
  if [ -z "$backupFile" ]; then
    exitError '1' 'backupFile (-b|--backupFile) must be specified when using script in Extract mode.' 'nc'
  fi

  # extract backupFile to outputLocation
  logInfo "Extracting backup file ($backupFile)"

  # create extraction target directory
  if [ ! -d "${outputLocation%/}/restore" ]; then
    # create subdirectory for restored files
    if ! mkdir "${outputLocation%/}/restore" >/dev/null 2>&1; then
      exitError '7' 'Could not create target subdirectory in output location.' 'nc'
    else
      logInfo "Extracted files will be written to '${outputLocation%/}/restore/'"
    fi
  else
    # ensure we can write to subdirectory for restored files
    if ! touch "${outputLocation%/}/restore/test.touch" >/dev/null 2>&1; then
      exitError '1' "Unable to write to output location: ${outputLocation%/}/restore/" 'nc'
    else
      rm -f "${outputLocation%/}/restore/test.touch" >/dev/null 2>&1
    fi
  fi

  # extract/decrypt backup file
  if [ "$decrypt" = 'true' ]; then
    logInfo 'Decrypting backup file before extraction'
    if ! gpg --pinentry-mode loopback --passphrase "${password}" -a --decrypt "${backupFile}" 2>>"$logfile" | tar --overwrite -xz -C "${outputLocation%/}/restore/" 2>>"$logfile"; then
      exitError '8' 'There was a problem extracting the backup file. Perhaps the file is NOT encrypted? If so, please run with the --extract flag instead of the --decrypt flag.' 'nc'
    else
      logSuccess "Backup extracted to '${outputLocation%/}/restore/'"
    fi
  else
    if ! tar --overwrite -xzf "$backupFile" -C "${outputLocation%/}/restore/" 2>>"$logfile"; then
      exitError '8' 'There was a problem extracting the backup file. Perhaps it is encrypted? If so, please run with --decrypt flag.' 'nc'
    else
      logSuccess "Backup extracted to '${outputLocation%/}/restore/'"
    fi
  fi

  # remove TLS configuration
  if [ "$removeTLS" = 'true' ]; then
    # get newest config file in outputLocation/restore directory
    cnConfigFile="$(find "${outputLocation%/}/restore/" -maxdepth 1 -name "config-*" -print 2>&1)"
    # remove TLS configuration entries
    logInfo 'task' "Removing TLS configuration from '$cnConfigFile'"
    sed -i -e '/^olcTLS/d' "$cnConfigFile" 2>>"$logfile"
    # check our work
    if ! grep -q '^olcTLS' "$cnConfigFile" 2>>"$logfile"; then
      logInfo 'done'
    else
      logInfo 'err'
      exitError '20' 'olcTLS entries could not be removed from restored config DIT LDIF file. You will have to do it manually.' 'nc'
    fi
  fi
elif [ $extract = 'false' ]; then
  ### process backup operations

  ## find ab-openldap container
  container=$(docker ps -a --no-trunc --filter "label=dev.asifbacchus.docker.internalName=ab-openldap" --format "{{ .Names }}")

  # check for null value -- cannot find container
  if [ -z "$container" ]; then
    exitError 2 'Cannot find ab-openldap container. Exiting.' 'nc'
  fi

  # check for multiple containers, exit if that's the case
  # N.B. do NOT quote $container or this loop will NOT work!!!
  # shellcheck disable=SC2086
  set -- dummy $container
  shift
  containerCount=0
  for c; do
    containerCount=$((containerCount + 1))
    logInfo "Found container($containerCount): $c"
  done
  if [ "$containerCount" -gt 1 ]; then
    exitError 2 'Multiple containers found. Exiting.' 'nc'
  fi

  ## backup databases to ldif

  # create temp working directory
  if ! docker exec "$container" mkdir "/$tempDir" >>"$logfile" 2>&1; then
    exitError 3 'Could not create temporary working directory.' 'nc'
  fi

  # backend to ldif
  logInfo 'task' 'Backup configuration database'
  if ! docker exec "$container" sh -c \
    "slapcat -F /etc/openldap/ldif -n 0 -l ${tempDir}/config-${fileDate}.ldif" \
    >>"$logfile" 2>&1; then
    logInfo 'err'
    exitError 4 'Could not backup configuration database.'
  fi
  logInfo 'done'

  # iterate frontend databases and export to ldif
  i=1
  while [ "$i" -ne "$numFrontEnd" ]; do
    logInfo 'task' "Backup frontend database $i"
    if ! docker exec "$container" sh -c \
      "slapcat -F /etc/openldap/ldif -n ${i} -l ${tempDir}/mdb${i}-${fileDate}.ldif" \
      >>"$logfile" 2>&1; then
      logInfo 'err'
      exitError 4 "Could not backup frontend database $i."
    else
      logInfo 'done'
    fi
    i=$((i + 1))
  done

  # compress and encrypt exported ldif files
  if [ $noEncryption = 0 ]; then
    logInfo 'task' 'Compressing and encrypting backup'
    if ! docker exec -w "/$tempDir" "$container" sh -c \
      "tar -czf - * | gpg --pinentry-mode loopback --passphrase ${password} -a --symmetric --cipher-algo AES256 -o ./ldap-${fileDate}.tar.gz" \
      >>"$logfile" 2>&1; then
      logInfo 'err'
      exitError 5 'Could not securely archive backup files.'
    fi
    logInfo 'done'
  elif [ $noEncryption = 1 ]; then
    logInfo 'task' 'Compressing backup'
    if ! docker exec -w "/$tempDir" "$container" sh -c \
      "tar -czf ldap-${fileDate}.tar.gz *" \
      >>"$logfile" 2>&1; then
      logInfo 'err'
      exitError 5 'Could not archive backup files.'
    fi
    logInfo 'done'
  fi

  ## copy file to output location on host
  logInfo 'task' 'Copying archive from docker container to host'
  if ! docker cp "$container:/$tempDir/ldap-${fileDate}.tar.gz" "$outputLocation/" \
    >>"$logfile" 2>&1; then
    logInfo 'err'
    exitError 6 'Unable to copy backup archive from container to host.'
  fi
  logInfo 'done'
  logInfo "Backup file: ${outputLocation%/}/ldap-${fileDate}.tar.gz"

  ## cleanup and log success
  cleanup
  logSuccess 'Backup completed successfully.'
fi

### clean up old backups
if [ "$deleteBackupsOlderThan" -gt 0 ]; then
  logInfo 'task' 'Cleaning up old backups'
  if ! find "${outputLocation%/}/" -type f -name "ldap-*.tar.gz" -mtime "+${deleteBackupsOlderThan}" -exec rm -f {} + >/dev/null 2>&1; then
    logInfo 'err'
    exitError 10 'Unable to remove old backup files.'
  fi
  logInfo 'done'
  logSuccess 'Old backup files removed successfully.'
fi

### exit gracefully
logSuccess 'All processes completed'
printf "%s[%s] --- %s execution completed ---\n%s" \
  "$magenta" "$(stamp)" "$scriptName" "$norm" >>"$logfile"
exit 0

# error code reference:
# 0: exited normally, no errors
# 1: parameter or permissions error
# 2: unsupported number of containers (0 or more than 1)
# 3: could not create/remove temporary directory/files
# 4: problem exporting database(s)
# 5: problem compressing/encrypting tar.gz archive
# 6: problem copying encrypted archive to host
# 7: problem creating target subdirectory in outputLocation
# 8: problem extracting gzipped tarball file
# 10: problem removing old backup files
# 20: problem removing TLS entries
# 99: dependencies not installed (tar, gzip)

#EOF
