#!/bin/bash

# read the linux repository location
. /etc/avast/vps.conf

PACKAGE="vps9"
REPO_URL="$URL/$PACKAGE"

AVASTDATADIR="/var/lib/avast"
AVASTBINARYPATH="/usr/lib/avast"
STORAGE_DIR="$AVASTDATADIR/Setup/filedir"
BSPATCH="$AVASTBINARYPATH/bspatch"
SUBMIT="$AVASTBINARYPATH/submit"
MD5SUM="md5sum"
DOWNLOAD="curl -L -s --connect-timeout 30 -f"
DOWNLOAD_RETRIES=2
DIFFAPPLY_RETRIES=2
PIDFILE="/run/avast/avast.pid"
LOCK_DIR="$AVASTDATADIR/Setup/lock"


do_download() {
    local i
    local FN="$1"
    for ((i=0; i<=$DOWNLOAD_RETRIES; i++)); do
        if [ $i -ne 0 ]; then
            echo "Downloading $FN... (retry $i)"
        fi
        $DOWNLOAD "$REPO_URL/$FN" > "$FN"
        STATUS=$?
        if [ "$STATUS" -eq 0 ]; then
            if [ -f "$FN" ]; then
                return 0
            fi
            STATUS=1
        fi
    done
    echo "Download failed [exit code $STATUS]"
    return $STATUS
}

do_patch_update() {
    VERSION=$(cat "$STORAGE_DIR/${PACKAGE}.lat")
    RAW="$STORAGE_DIR/${PACKAGE}.raw"

    if [ -z "${VERSION}"  -o  ! -f "${RAW}" ]; then
        echo "Starting raw package not present."
        return 1
    fi

    do_download "${PACKAGE}${VERSION}.inf"
    if [ $? -ne 0 ]; then
        return 1
    fi
    NEXT=$(grep "NEXT=" "${PACKAGE}${VERSION}.inf" | tail -n 1 | cut -d= -f2)

    while true; do
        if [ -z "${NEXT}" ]; then
            echo "Missing pointer to next patch."
            return 1
        fi

        for (( i=0; i<=$DIFFAPPLY_RETRIES; i++ )); do
            do_download "${PACKAGE}${NEXT}.inf"
            STATUS=$?
            if [ $STATUS -ne 0 ]; then
                continue
            fi

            do_download "${PACKAGE}_${VERSION}_${NEXT}.dif"
            STATUS=$?
            if [ $STATUS -ne 0 ]; then
                continue
            fi

            # apply bspatch utlity
            "$BSPATCH" "$RAW" "${PACKAGE}.tmp" "${PACKAGE}_${VERSION}_${NEXT}.dif" 2>/dev/null
            STATUS=$?
            if [ $STATUS -ne 0 ]; then
                rm -f "${PACKAGE}.tmp"
                rm "${PACKAGE}_${VERSION}_${NEXT}.dif"
                continue
            fi

            # check MD5s
            MD5=$(grep "MD5=" "${PACKAGE}${NEXT}.inf" | tail -n 1 | cut -d= -f2)
            if [ "$MD5" != $(${MD5SUM} ${PACKAGE}.tmp | cut -d ' ' -f1) ]; then
                echo "MD5 checksum does not match."
                rm "${PACKAGE}.tmp"
                rm "${PACKAGE}_${VERSION}_${NEXT}.dif"
                STATUS=1
                continue
            fi

            mv "${PACKAGE}.tmp" "${PACKAGE}.raw"
            STATUS=$?
            if [ $STATUS -ne 0 ]; then
                rm "${PACKAGE}.tmp"
                rm "${PACKAGE}_${VERSION}_${NEXT}.dif"
                continue
            fi

            RAW="${PACKAGE}.raw"
            VERSION="${NEXT}"
            STATUS=0
            break
        done

        if [ $STATUS -ne 0 ]; then
            return $STATUS
        fi

        if [ "${VERSION}" = "${LAT}" ]; then
            return 0
        fi

        do_download "${PACKAGE}${VERSION}.inf"
        if [ $? -ne 0 ]; then
            return 1
        fi
        NEXT=$(grep "NEXT=" "${PACKAGE}${VERSION}.inf" | tail -n 1 | cut -d= -f2)
    done
}

do_full_update() {
    do_download "${PACKAGE}${LAT}.inf"
    if [ $? -ne 0 ]; then
        return 1
    fi
    MD5=$(grep "MD5=" "${PACKAGE}${LAT}.inf" | tail -n 1 | cut -d= -f2)
    if [ -z "$MD5" ]; then
        return 1
    fi

    do_download "${PACKAGE}${LAT}.ful"
    if [ $? -ne 0 ]; then
        return 1
    fi

    gunzip -c "${PACKAGE}${LAT}.ful" > "${PACKAGE}.tmp"
    if [ $? -ne 0 ]; then
        return 1
    fi

    if [ "$MD5" != $(${MD5SUM} ${PACKAGE}.tmp | cut -d ' ' -f1) ]; then
        echo "MD5 checksum does not match."
        return 1
    fi

    mv "${PACKAGE}.tmp" "${PACKAGE}.raw"
    if [ $? -ne 0 ]; then
        return 1
    fi

    VERSION="${LAT}"
    return 0
}

do_update_vps() {
    VERSION=$(cat "$STORAGE_DIR/${PACKAGE}.lat")
    RAW="$STORAGE_DIR/${PACKAGE}.raw"

    if [ -z "${VERSION}"  -o  ! -f "${RAW}" ]; then
        echo "Starting raw package not present."
        return 1
    fi

    if [ -d "$AVASTDATADIR/defs/$VERSION" ]; then
        echo "Version already installed?"
        return 1
    fi

    mkdir -p "$AVASTDATADIR/defs/$VERSION"
    if [ $? -ne 0 ]; then
        echo "Cannot create directory $AVASTDATADIR/defs/$VERSION"
        return 1
    fi

    tar -x -C "$AVASTDATADIR/defs/$VERSION" -f "$RAW"
    if [ $? -ne 0 ]; then
        echo "Cannot unpack the update package."
        rm -rf "$AVASTDATADIR/defs/$VERSION"
        return 1
    fi

    TMPASWDEFS="$(dirname ${RAW})/aswdefs.ini"
    echo "[Definitions]" >"$TMPASWDEFS"
    echo "Latest=$VERSION" >>"$TMPASWDEFS"

    mv "$TMPASWDEFS" "$AVASTDATADIR/defs/aswdefs.ini"

    return 0
}

update() {
    # Download the newest VPS package
    echo "Connecting to repository $REPO_URL"

    if [ -f "$STORAGE_DIR/${PACKAGE}.lat" ]; then
        VERSION=$(cat "$STORAGE_DIR/${PACKAGE}.lat")
    fi

    do_download "${PACKAGE}.lat"
    if [ $? -ne 0 ]; then
        echo "Update aborted."
        return 1
    fi

    LAT=$(cat "${PACKAGE}.lat")
    if [ -z "$LAT" ]; then
        echo "Cannot retrieve info about the latest version."
        return 1
    fi
    if [ "$LAT" = "$VERSION" ]; then
        echo "VPS is up to date."
        return 2
    else
        echo "New VPS version: $LAT"
    fi

    STATUS=1
    # Do incremental update, if we have all prerequisites:
    if [ -f "$STORAGE_DIR/$PACKAGE.raw"  -a  -f "$STORAGE_DIR/$PACKAGE.lat" ]; then
        do_patch_update
        STATUS=$?
    fi

    # Fall back to full update:
    if [ $STATUS -ne 0 ]; then
        do_full_update
        STATUS=$?
        if [ $STATUS -ne 0 ]; then
            echo "Update failed."
            return 1
        fi
    fi

    # Update the storage (or render it incomplete on a failure):
    rm -f "$STORAGE_DIR/${PACKAGE}.raw" "$STORAGE_DIR/${PACKAGE}.lat" 2>/dev/null
    mv -f "${PACKAGE}.raw" "${PACKAGE}.lat" "$STORAGE_DIR" 2>/dev/null
    STATUS=$?
    if [ $STATUS -ne 0 ]; then
        echo "Cannot replace ${PACKAGE}.raw and ${PACKAGE}.lat files"
        return 1
    fi

    return 0
}

restart_avast()
{
    DAEMONPID=$(cat "$PIDFILE" 2>/dev/null)
    if [ -n "$DAEMONPID" ] && kill -0 $DAEMONPID 2>/dev/null; then
        # inform the daemon about finished update
        kill -HUP "$DAEMONPID" 2>/dev/null
    else
        # remove obsolete VPS directories
        CURVERSION=$(grep -e "Latest=" $AVASTDATADIR/defs/aswdefs.ini 2>/dev/null | cut -c 8-)
        for DEFDIR in $(ls -d $AVASTDATADIR/defs/????????); do
            if [ "$DEFDIR" != "$AVASTDATADIR/defs/$CURVERSION" ]; then
                rm -rf "$DEFDIR"
            fi
        done
    fi

    return 0
}


# create storage directory
if [ ! -d "$STORAGE_DIR" ]; then
    mkdir -p "$STORAGE_DIR"
fi

# create lock directory to prevent parallel run
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
    exit 0
fi

# create temporary work directory
TEMP_DIR="$(mktemp -d --tmpdir=/tmp -q -t avast.download-XXXXX)"

# setup lock & temp directory cleanup
trap "rm -rf \"$TEMP_DIR\" \"$LOCK_DIR\"" EXIT SIGTERM

cd "$TEMP_DIR"

# perform update
update
STATUS=$?
if [ $STATUS -eq 1 ]; then
    # Download/package error
    exit 1
elif [ $STATUS -eq 2 ]; then
    # VPS up to date
    :
else
    # New VPS available, update to it
    if do_update_vps; then
        restart_avast
    fi
fi

# send submits if any
$SUBMIT -s

exit 0
