#!/bin/sh
###############################################################################
#                                                                             #
#                           Update gw software                                #
#                                                                             #
###############################################################################

. /usr/local/bin/telem/functions

boardname="$(/usr/local/bin/telem/boardname.sh)"

###############################################################################
#                                                                             #
#                         Gateway6 files and folders                          #
#                                                                             #
###############################################################################

FOLDER_TEMP='/tmp/upd_tar' # Temporary files
# VMX25 with 64MB of RAM will not be enough if extracted to /tmp
[ "$boardname" == "VMX25" ] && FOLDER_TEMP='/home/martem/upd_tar'

SDD='/mnt/sd'
UPDATE_FOLDER='notset'
UPD_ROOT="${FOLDER_TEMP}/gwupd"
UPD_LOG="${FOLDER_TEMP}/res.txt"
ERR_LOG="${FOLDER_TEMP}/err.txt"

kernel_update=0

STATS_DIR='/var/local/telem/stats'

fwverified='?'

###############################################################################
#                                                                             #
#                             Script functions                                #
#                                                                             #
###############################################################################

# Write message to update log and syslog
# $1 - message
updlog() {
    echo "$(date) - $1" >> "${UPD_LOG}"
    logg "$1"
}

# Write error to update log and syslog
# $1 - message
errlog() {
    echo    "$(date) - $1" >> "${ERR_LOG}"
    loggerr "$1"
}

# clears update directory
clear_update_dir() {
    if [ ! -e "${UPD_ROOT}" ]; then
        return 1
    fi
    rm -r "${UPD_ROOT}"
}

upd_done() {
    if [ "${1}" != "/mnt/sd/gwupd" ]; then
        mv "${1}" "${1}_done"
        rm -f "${1}_done"/upd.*
    fi
    clear_update_dir
}

rmdirtykey() {
    local dirtykey='/etc/telem/keys/dirty.pub'
    [ "${fwverified}" = 'clean' ] && rm -f "${dirtykey}" 2>/dev/null
}

reloadconf() {
    local SETUP_FILENAME_NEW='/usr/local/etc/telem/setup.new.tar.xz'
    local SETUP_FILENAME='/usr/local/etc/telem/setup.tar.xz'

    if [ -e "${SETUP_FILENAME}" ]; then
        # if tar.xz config file exists, reload it
        logger -s -p "user.info" -t "$0" "Reload ${SETUP_FILENAME##*/}"
        mv "${SETUP_FILENAME}" "${SETUP_FILENAME_NEW}"
    else
        logger -s -p "user.info" -t "$0" "Config not found for reload"
    fi
}

verifySig() {
    local KEY="$1"
    local TARGET="$2"
    test -f "${KEY}"        || return 1
    test -f "${TARGET}"     || return 1
    test -f "${TARGET}.sig" || return 1
    while read -r sig || [[ -n "$sig" ]]; do
        echo "$sig" | openssl enc -d -base64 -A | \
        openssl dgst -sha384 -keyform DER -verify "${KEY}" -signature /dev/stdin "${TARGET}" && \
        return 0
    done < "${TARGET}.sig"
    return 1
}

verify() {
    local SRC_DIR="$1"

    if ls /etc/telem/keys/*.pub &>/dev/null; then
        local TARGET="${SRC_DIR}/checksums.sha512"
        fwverified='key'
        for key in clean dirty; do
            if verifySig "/etc/telem/keys/${key}.pub" "${TARGET}"; then
                # signature is correct
                fwverified="${key}"
                (
                    # checksums have to be correct and contain upd.tar
                    set -o pipefail
                    sha512sum -c "${TARGET}" | grep -Fq -- './upd.tar: OK'
                ) && return 0
                # checksum failed
                break
            fi
        done
    else
        # Old md5sum check
        fwverified='md5'
        md5sum -s -c "$SRC_DIR/md5sums" && return 0
    fi
    return 1
}

# extract software
extractsw() {
    # location of tarball
    local SRC_DIR="$1"

    clear_update_dir

    if [ ! -e "${SRC_DIR}" ]; then
        return 1
    fi

    test -e "${SRC_DIR}/Kernel" && kernel_update=1

    cd "${SRC_DIR}"
    if verify "${SRC_DIR}"; then
        logger -s -p "user.info" -t "$0" "Verified OK (${fwverified})"

        # detect archive type and select approriate filter
        if [ -e "${SRC_DIR}/upd.tar" ]; then
            ARC_FILTER=""
            TARBALL="${SRC_DIR}/upd.tar"
        elif [ -e "${SRC_DIR}/upd.tar.gz" ]; then
            ARC_FILTER="-z"
            TARBALL="${SRC_DIR}/upd.tar.gz"
        elif [ -e "${SRC_DIR}/upd.tar.bz2" ]; then
            ARC_FILTER="-j"
            TARBALL="${SRC_DIR}/upd.tar.bz2"
        elif [ -e "${SRC_DIR}/upd.tar.lzma" ]; then
            ARC_FILTER="-a"
            TARBALL="${SRC_DIR}/upd.tar.lzma"
        fi

        logger -s -p "user.info" -t "$0" "Entering temporary folder"
        mkdir -p "${FOLDER_TEMP}"
        cd "${FOLDER_TEMP}"
        logger -s -p "user.info" -t "$0" "Extracting tarball"
        tar -x $ARC_FILTER -f "${TARBALL}"

    else
        errlog "Verification failed (${fwverified}), aborting update!"
        upd_done "${SRC_DIR}"
    fi
}

checkIfBadUpdate() {
    # check if hash is in "/etc/.badfw"
    [ -f "$1/md5sums" ] || return 0
    local hash=$(awk '/upd/ {print $1}' "$1/md5sums")
    [ -f "/etc/.badfw" ] && grep -Fq -- "$hash" "/etc/.badfw"
}

run_update() {
    if [ -e "${UPD_ROOT}/upd.sh" ]; then
        # Update script exists, allow execution and execute
        export UPGRADED=1
        chmod +x "${UPD_ROOT}/upd.sh"

        logger -s -p "user.info" -t "$0" "Starting update script: $UPD_ROOT/upd.sh"

        local _version_before="$(cat '/usr/local/etc/telem/version')"
        "${UPD_ROOT}/upd.sh" "${UPD_ROOT}" "${UPD_LOG}" "${ERR_LOG}"
        local _version_after="$(cat '/usr/local/etc/telem/version')"

        if [ "_$_version_before" = "_$_version_after" ]; then
            # TODO: proper fix
            # Hard to tell if version did not change because:
            # 1) (good) firmware was found in done file and was not updated
            # 2) (bad)  firmware was not found in done file, but has same firmware version and was updated!!
            # Last one happens commonly with fresly flashed devices.
            # Currently workaround is available in new firmware UpdateSystem.sh script (called by $UPD_ROOT/upd.sh)
            return 0
        fi

        [ "${kernel_update}" = "1" ] && return 0

        rm -f '/etc/network/interfaces' # ??

        rmdirtykey
        reloadconf

        chmod +x '/usr/local/bin/telem/check_permissions'

        /usr/local/bin/telem/check_permissions /

        clear_update_dir

        /usr/local/bin/telem/post_update.sh
    fi
}

setUpdateDirectory() {
    UPDATE_FOLDER="$1"
    [ -e "${UPDATE_FOLDER}" ] || return 1
    ERR_LOG="${UPDATE_FOLDER}/err.txt"
    UPD_LOG="${UPDATE_FOLDER}/res.txt"
    return 0
}

LocateUpdateDirectory() {
    # update from SD?
    if setUpdateDirectory "${SDD}/gwupd"; then
        # skip this to the end, if backup directory is set
        [ -d "${SDD}/gwupd-bkp/" ] || return 0
    fi

    # look inside /root folder
    setUpdateDirectory '/root/gwupd' && return

    # look inside /home/*user* folders
    for dir_name in /home/*; do
        if [ -d "${dir_name}" ]; then
            setUpdateDirectory "${dir_name}/gwupd" && return
        fi
    done

    # last resort: use SD card
    setUpdateDirectory "${SDD}/gwupd" && return

    UPDATE_FOLDER='notset'
    return 1
}

Keep7z() {
    [ -e '/etc/init.d/stored-fw' ] || return 0
    # Storing 7z is enabled
    [ -r '/root/firmware.new.web.7z' ] || return 0
    # 7z file exists
    [ -r '/root/gwupd/VERSION' ] || return 0
    # Required file for renaming is available
    read a b c 2>/dev/null < '/root/gwupd/VERSION'
    [ "Telem" = "${a::5}" ] && [ -n "$c" ] || return 0
    # All version parts are available
    mv '/root/firmware.new.web.7z' "/root/gwupd/${a}_${c}_${b}.7z"
}

Locate7zUpdate() {
    command -v 7zr &>/dev/null || return 1
    local ret=1
    if [ -f '/root/firmware.new.web.7z' ]; then
        mkdir '/root/gwupd.web.tmp/' && \
        7zr x '/root/firmware.new.web.7z' -o'/root/gwupd.web.tmp/' && \
        mv '/root/gwupd.web.tmp/gwupd' '/root/gwupd' && \
        setUpdateDirectory '/root/gwupd' && \
        ret=0
    fi

    Keep7z

    # cleanup
    rm '/root/firmware.new.web.7z'     &>/dev/null
    rm '/root/firmware.new.web.7z.tmp' &>/dev/null
    rm -r '/root/gwupd.web.tmp/'       &>/dev/null

    return $ret
}

###############################################################################
#                                                                             #
#                                The Script                                   #
#                                                                             #
###############################################################################

start() {

    if [ -e "${STATS_DIR}/device-is-booted" ]; then
        logger -s -p "user.info" -t "$0" "System already booted, restaring before startig update procedure"
        reboot
        exit 1
    fi

    logger -s -p "user.info" -t "$0" "Starting telem-update"

    ! umount "${SDD}"
    rm -r "${SDD}"

    # mount SD card if connected
    mountSDp1 || ln -s '/dev/null' "${SDD}"

    # locate folder with new update
    if LocateUpdateDirectory || Locate7zUpdate; then
        if [ "${UPDATE_FOLDER}" != "notset" ]; then
            logger -s -p "user.info" -t "$0" "Update folder is: '${UPDATE_FOLDER}'"

            if checkIfBadUpdate "${UPDATE_FOLDER}"; then
                updlog "Bad or old firmware, stop update"
                upd_done "${UPDATE_FOLDER}"
            else
                extractsw "${UPDATE_FOLDER}"
                run_update
            fi
        fi
    fi

    logger -s -p "user.info" -t "$0" "telem-update finished"
}

stop() {
    return 0
}

restart() {
    return 0
}

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart|reload)
    restart
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
esac

exit $?
