#!/bin/sh

#
# update script for ab-nginx container and utility scripts
#   version 2.1.0
#   script by Asif Bacchus
#

#
# functions
errMsg() {
    printf "\n%s%s%s\n\n" "$err" "$1" "$norm"
    [ -n "$2" ] && exit "$2" || exit 1
}

errNotify() {
    printf "%s[ERROR]%s\n" "$err" "$norm"
}

okMsg() {
    printf "%s%s%s\n\n" "$ok" "$1" "$norm"
}

okNotify() {
    printf "%s[OK]%s\n" "$ok" "$norm"
}

makeDirectory() {
    if ! [ -d "./$1" ]; then
        if ! mkdir "./$1" >/dev/null 2>&1; then
            errMsgString="Unable to make '$1' directory."
            errMsg "$errMsgString" 40
        fi
    fi
}

scriptHelp() {
    textNewline
    textBlock "Update ${containerName} container and helper script files"
    textBlock "${bold}Usage: ${localScriptName} [parameters]${norm}"
    textNewline
    textBlock "If run with no parameters, the script will update both the container and the helper script files, including this update script."
    textBlockHeader " parameters "
    textBlockParam "-h | -? | --help" "Display this help screen."
    textBlockParam "-c | --container | --container-only" "Update the docker container only."
    textBlockParam "-s| --scripts |--scripts-only" "Update the helper scripts (including this update script) only."
    textNewline
    exit 0
}

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

textBlockHeader() {
    printf "\n%s***%s***%s\n" "$header" "$1" "$norm"
}

textBlockParam() {
    printf "%s%-35s%s%s\n" "$info" "$1" "$2" "$norm"
}

textNewline() {
    printf "\n"
}

#
# text formatting presets
if command -v tput >/dev/null 2>&1; then
    bold=$(tput bold)
    err=$(tput bold)$(tput setaf 1)
    info=$(tput bold)$(tput setaf 6)
    header=$(tput bold)$(tput setaf 5)
    norm=$(tput sgr0)
    ok=$(tput sgr0)$(tput setaf 2)
    warn=$(tput bold)$(tput setaf 3)
    width=$(tput cols)
else
    bold=''
    err=''
    info=''
    header=''
    norm=''
    ok=''
    warn=''
    width=80
fi

#
# pre-requisites
# check if wget is installed
if ! command -v wget >/dev/null 2>&1; then
    errMsg "Sorry, this script requires that 'wget' is installed in order to download updates. Exiting."
fi

# zero counters
updatesAvailable=0
downloadFailed=0
downloadSuccess=0
updateFailed=0
updateSuccess=0

# reference constants
dockerNamespace='nginx'
containerName='ab-nginx'
containerUpdatePath="docker.asifbacchus.dev/$dockerNamespace/$containerName:latest"
server="https://asifbacchus.dev/public/docker/$dockerNamespace/$containerName/"
checksumFilename='checksums.sha256'

# operation triggers
doDockerUpdate=1
doScriptUpdate=1

# files to update
localScriptName="$(basename "$0")"
repoScriptName='update.sh'

#
# process startup parameters
while [ $# -gt 0 ]; do
    case "$1" in
    -h | -\? | --help)
        # display inline help
        scriptHelp
        ;;
    -s | --scripts | --scripts-only)
        # update scripts only, skip docker container update
        doDockerUpdate=0
        ;;
    -c | --container | --container-only)
        # update docker container only, skip script update
        doScriptUpdate=0
        ;;
    *)
        printf "%s\nUnknown option: %s\n" "$err" "$1"
        printf "%sUse '--help' for valid options%s\n\n" "$info" "$norm"
        exit 1
        ;;
    esac
    shift
done

#
# update container
if [ "$doDockerUpdate" -eq 1 ]; then
    # check if docker is installed
    if ! command -v docker >/dev/null 2>&1; then
        errMsg "Sorry, it appears that docker is not installed on this machine! Exiting." 2
    fi

    # is user root or in the docker group?
    if [ ! "$(id -u)" -eq 0 ]; then
        if ! id -Gn | grep docker >/dev/null; then
            errMsg "You must either be root or in the 'docker' group to pull container updates." 2
        fi
    fi

    printf "%s\n*** Updating %s container ***\n\n%s" "$info" "$containerName" "$norm"
    if ! docker pull "$containerUpdatePath"; then
        errMsg "There was an error updating the container. Try again later." 31
    else
        okMsg "Container updated!"
    fi
fi

#
# update scripts
if [ "$doScriptUpdate" -eq 1 ]; then
    printf "%s*** Updating %s helper scripts ***%s\n" "$info" "$containerName" "$norm"

    ## download latest checksums
    printf "Getting latest checksums... "
    if ! wget --quiet --tries=3 --timeout=10 -N "${server}${checksumFilename}"; then
        errNotify
        errMsg "Unable to download checksums. Try again later." 41
    else
        okNotify
    fi

    ## check for updates to this script
    printf "Checking for updates to this script... "
    repoScriptChecksum=$(grep "$repoScriptName" "$checksumFilename" | grep -o '^\S*')
    localScriptChecksum=$(sha256sum "$localScriptName" | grep -o '^\S*')
    if [ "$localScriptChecksum" = "$repoScriptChecksum" ]; then
        printf "[NONE]\n"
    else
        printf "[AVAILABLE]\n"
        printf "Getting updated script... "
        # download updated script
        if ! wget --quiet --tries=3 --timeout=10 -O "update.sh.tmp" "${server}${repoScriptName}"; then
            errNotify
            # delete failed download as necessary
            rm -f ./update.sh.tmp 2>/dev/null
            errMsg "Unable to download script update. Try again later." 42
        else
            # verify download
            dlScriptChecksum=$(sha256sum "update.sh.tmp" | grep -o '^\S*')
            if ! [ "$dlScriptChecksum" = "$repoScriptChecksum" ]; then
                printf "[ERROR]\n"
                # delete corrupt download as necessary
                rm -f ./update.sh.tmp 2>/dev/null
                errMsg "Checksum mismatch! Try again later." 42
            else
                okNotify
                printf "\n%s*** This script has been updated. Please re-run it to load the updated version of this file. ***%s\n\n" "$warn" "$norm"
                # overwrite this script with updated script
                mv -f ./update.sh.tmp "$localScriptName"
                chmod +x "$localScriptName"
                exit 0
            fi
        fi
    fi

    ## update files
    makeDirectory "config"
    makeDirectory "sites"
    makeDirectory "snippets"

    while IFS='  ' read -r field1 field2; do
        if [ "$field2" = "update.sh" ]; then continue; fi
        printf "\nChecking '%s' for updates... " "$field2"
        updateFilename="$field2"
        repoFileChecksum="$field1"
        if [ -f "$updateFilename" ]; then
            localFileChecksum=$(sha256sum "$updateFilename" | grep -o '^\S*')
        else
            localFileChecksum=0
        fi

        # update file if necessary
        if ! [ "$localFileChecksum" = "$repoFileChecksum" ]; then
            printf "[AVAILABLE]\n"
            updatesAvailable=$((updatesAvailable + 1))
            # download update
            printf "Downloading updated '%s'... " "$updateFilename"
            if ! wget --quiet --tries=3 --timeout=10 -O "$updateFilename.tmp" "${server}${updateFilename}"; then
                errNotify
                downloadFailed=$((downloadFailed + 1))
                # delete failed download file as necessary
                rm -f "$updateFilename.tmp" 2>&1
            else
                okNotify
                downloadSuccess=$((downloadSuccess + 1))
                # verify download
                printf "Verifying '%s'... " "$updateFilename"
                localFileChecksum=$(sha256sum "$updateFilename.tmp" | grep -o '^\S*')
                if ! [ "$localFileChecksum" = "$repoFileChecksum" ]; then
                    errNotify
                    updateFailed=$((updateFailed + 1))
                    # delete corrupted download file as necessary
                    rm -f "$updateFilename.tmp" 2>&1
                else
                    okNotify
                    updateSuccess=$((updateSuccess + 1))
                    # overwrite old version of file
                    mv -f "$updateFilename.tmp" "$updateFilename"
                    if [ "${updateFilename##*.}" = "sh" ]; then
                        # shell file --> make it executable
                        chmod +x "$updateFilename"
                    fi
                fi
            fi
        else
            printf "[NONE]\n"
        fi
    done <"$checksumFilename"
fi

#
# display results
if [ "$doScriptUpdate" -eq 1 ]; then
    printf "\n%s*** Results ***%s\n" "$info" "$norm"
    printf "\tUpdates: %s available\n" "$updatesAvailable"
    printf "\tDownloads: %s%s successful%s, %s%s failed%s\n" "$ok" "$downloadSuccess" "$norm" "$err" "$downloadFailed" "$norm"
    printf "\tUpdates: %s%s applied%s, %s%s failed%s\n" "$ok" "$updateSuccess" "$norm" "$err" "$updateFailed" "$norm"
fi

#
# exit
if [ "$downloadFailed" -gt 0 ]; then
    exit 43
elif [ "$updateFailed" -gt 0 ]; then
    exit 44
else
    exit 0
fi
# this is a trap for mis-coding... should never get an exit code 99!
exit 99

#
# exit return codes
# 0:        normal exit, no errors
# 1:        missing or invalid parameter
# 2:        docker not found or no docker permissions
# 31:       unable to update docker container
# 4x:       helper files errors
#   40:     unable to make download directories
#   41:     unable to download checksums
#   42:     update script: unable to download or bad checksum
#   43:     update helpers: unable to download
#   44:     update helpers: bad checksum, no update
# 99:       coding mistake trap -- this return code should never happen!

#EOF
