#!/bin/sh
#
# Written by Russell Cumber<Russell.Cumber@daktronics.com>

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin

ErrorLED()
{
    /etc/init.d/ledcio restart 60
}

# Return success only when the system booted less than 5 minutes ago.
IsWithinBootWindow()
{
    UPTIME_SECONDS=`cut -d. -f1 /proc/uptime 2>/dev/null`
    if expr "$UPTIME_SECONDS" : '[0-9]\+$' >/dev/null 2>&1; then
        if [ "$UPTIME_SECONDS" -lt 300 ]; then
            return 0
        fi
    fi
    return 1
}

# Reject empty, locked/disabled (!, !!, *), or truncated hashes. Mirror the
# rescuebutton hashLooksUsable check: require a leading '$' and at least three '$'
# characters so usermod does not restore an unusable password.
HashLooksUsable()
{
    HASH="$1"
    case "$HASH" in
        \$*) ;;          # must start with '$'
        *) return 1 ;;
    esac
    DOLLAR_COUNT=`printf '%s' "$HASH" | tr -cd '$' | wc -c`
    if [ "$DOLLAR_COUNT" -ge 3 ]; then
        return 0
    fi
    return 1
}

# Restore the Dak factory password when a marker file is present on the USB.
RestoreFactoryPassword()
{
    MOUNTPOINT="$1"

    # Trigger: empty marker file. Only its existence matters.
    if [ ! -e "$MOUNTPOINT/vip5060/reset-password" ]; then
        return 0
    fi

    logger -st "Recovery USB" "Password reset marker found"

    # Enforce the early-boot window (fail closed on parse errors).
    if ! IsWithinBootWindow; then
        logger -st "Recovery USB" "Password reset ignored: outside boot window"
        return 0
    fi

    RESTORED=0
    FACTORY_SHADOW_PATH="/home/Dak/.settings/factory-shadow"
    if [ -f "$FACTORY_SHADOW_PATH" ]; then
        HASH=`grep '^Dak:' "$FACTORY_SHADOW_PATH" | head -1 | cut -d: -f2`
        if HashLooksUsable "$HASH"; then
            if usermod -p "$HASH" Dak; then
                logger -st "Recovery USB" "Password restored from factory backup"
                RESTORED=1
            else
                logger -st "Recovery USB" "usermod failed applying factory backup"
            fi
        else
            logger -st "Recovery USB" "Factory backup hash invalid, using default"
        fi
    else
        logger -st "Recovery USB" "No factory backup found, using default"
    fi

    if [ "$RESTORED" != 1 ]; then
         DEFAULT_SHADOW_PATH="/home/Dak/.settings/default-shadow"
         if [ -f "$DEFAULT_SHADOW_PATH" ]; then
             DEFAULT_HASH=`grep '^Dak:' "$DEFAULT_SHADOW_PATH" | head -1 | cut -d: -f2`
             if HashLooksUsable "$DEFAULT_HASH"; then
                 if usermod -p "$DEFAULT_HASH" Dak; then
                     logger -st "Recovery USB" "Password reset to default"
                     RESTORED=1
                 else
                     logger -st "Recovery USB" "usermod failed applying default hash"
                 fi
             else
                 logger -st "Recovery USB" "Default hash invalid"
             fi
         else
             logger -st "Recovery USB" "No default hash file found"
         fi
     fi

     if [ "$RESTORED" = 1 ]; then
         # Refresh credentials from the updated shadow file. Use a temporary file to
         # ensure atomicity and preserve ownership/permissions. Only report success
         # after the refresh completes successfully.
         HTPASSWD_TMP="/etc/.htpasswd.tmp.$$"
         if grep '^Dak:' /etc/shadow > "$HTPASSWD_TMP" 2>/dev/null && [ -s "$HTPASSWD_TMP" ]; then
             # Preserve ownership (root:root) and mode (644) of the original file.
             chmod 644 "$HTPASSWD_TMP"
             if mv "$HTPASSWD_TMP" /etc/.htpasswd; then
                 logger -st "Recovery USB" "Credentials refreshed successfully"
                 # Confirm blink (matches the config-restore success indication).
                 /etc/init.d/ledcio restart 0.1
             else
                 logger -st "Recovery USB" "Failed to install refreshed credentials"
                 rm -f "$HTPASSWD_TMP"
                 ErrorLED
             fi
         else
             logger -st "Recovery USB" "Failed to refresh credentials from shadow"
             rm -f "$HTPASSWD_TMP"
             ErrorLED
         fi
     else
         ErrorLED
     fi
    # Note: the marker is intentionally left in place. The USB is treated as
    # read-mostly and may be write-protected, so we do not assume we can delete it.
}

LoadBackupFromFile()
{
    logger -st "Recovery USB" "Loading Backup Configuration"
    mkdir -p /home/Dak/.settings/recoveryUSB
    rm -rf /home/Dak/.settings/recoveryUSB/*
    /usr/bin/unzip "$1" -d /home/Dak/.settings/recoveryUSB

    if [ -f /home/Dak/.settings/recoveryUSB/TranslationTable.hex ]; then
        RESULT=`/usr/bin/curl -i -X PUT -H "Content-Type: application/vnd.daktronics.translationtable.rev3" --data-binary @"/home/Dak/.settings/recoveryUSB/TranslationTable.hex" http://localhost:$PORT/daktronics/syscontrol/1.0/ttable/0 | grep "HTTP/1.1 20"`
        if [ "$RESULT" == "" ]; then
            logger -st "Recovery USB" "Error Loading TTable"
            ErrorLED
            return 1
        fi
    else
        logger -st "Recovery USB" "Skipping TTable, File Not Found on USB"
    fi

    if [ -f /home/Dak/.settings/recoveryUSB/Configuration.xml ]; then
        RESULT=`/usr/bin/curl -i -X PUT -H "Content-Type: application/xml" --data-binary @"/home/Dak/.settings/recoveryUSB/Configuration.xml" http://localhost:$PORT/daktronics/syscontrol/1.0/configuration | grep "HTTP/1.1 20"`
        if [ "$RESULT" == "" ]; then
            #if the port changed, could be a false negative, try again
            NEW_PORT=`sed '/xsi:type="ManagementServiceType">/,/<\/Service>/!d;/<Port>/!d;s/ *<\/\?Port> *//g' /home/Dak/.settings/recoveryUSB/Configuration.xml`
            if [ $PORT -ne $NEW_PORT ]; then
                sleep 5
                RESULT=`/usr/bin/curl -i -X PUT -H "Content-Type: application/xml" --data-binary @"/home/Dak/.settings/recoveryUSB/Configuration.xml" http://localhost:$NEW_PORT/daktronics/syscontrol/1.0/configuration | grep "HTTP/1.1 20"`
                if [ "$RESULT" == "" ]; then
                    logger -st "Recovery USB" "Error Loading Config on Port $NEW_PORT"
                    ErrorLED
                    return 1
                fi
            else
                logger -st "Recovery USB" "Skipping Configuration, File Not Found on USB"
                ErrorLED
                return 1
            fi
        fi
    else
        logger -st "Recovery USB" "Error Loading Configuration.xml"
    fi

    return 0
}

SaveCurrentConfiguration()
{
    HOSTNAME=`hostname`
    logger -st "Recovery USB" "Saving Current Configuration to $1/$HOSTNAME.zip"
    
    /usr/bin/curl -o $1/Configuration.xml http://localhost:$PORT/daktronics/syscontrol/1.0/configuration
    /usr/bin/curl -o $1/TranslationTable.hex http://localhost:$PORT/daktronics/syscontrol/1.0/ttable/0
        
    rm -f $1/HOSTNAME.zip
    /usr/bin/zip -m -j $1/$HOSTNAME $1/Configuration.xml $1/TranslationTable.hex
    sync;
    return 0
}

#todo:: this should detect hardware ID and select different folder
FindBackupFile()
{
    logger -st "Recovery USB" "Scanning $1"
    FILE=`find $1/vip5060/*.zip | head -1`
    if [ "$FILE" ]; then 
        logger -st "Recovery USB" "Found: $FILE"
        mkdir -p $1/vip5060/.backup
        SaveCurrentConfiguration $1/vip5060/.backup
        return 1
    else
        logger -st "Recovery USB" "Error - Backup File Not Found"
        return 0
    fi
}

GetManagementPort()
{
  PORT=`sed '/xsi:type="ManagementServiceType">/,/<\/Service>/!d;/<Port>/!d;s/ *<\/\?Port> *//g' /home/Dak/.settings/daktronics/syscontrol/1.0/configuration/configuration`
  if expr "$PORT" : '[0-9]\+$' >/dev/null
  then
    logger -st "Recovery USB" "Using Port $PORT"
  else
    #not valid integer, parsing failed
    PORT=85;
    logger -st "Recovery USB" "Error Reading Port. Defaulting to $PORT"
  fi
}


SaveLogs()
{
    logger -st "SaveLogs" "Started"
    FILE='/home/Dak/.settings/logs.tar.gz'
    UDIR='/media/sda1/Logs'
    LDIR='/media/sda1/logs'
    HOSTNAME=`hostname`
    logger -st "SaveLogs" "Saving for $HOSTNAME"
    if [[ -d "$UDIR" ]] || [[ -d "$LDIR" ]]; then
        logger -st "SaveLogs" "found logs directory on USB drive, running logcollector"
        logcollector
        if [ "$FILE" ]; then 
            logger -st "SaveLogs" "found logs.tar.gz"
        else
            logger -st "SaveLogs" "did not find logs.tar.gz"
            return 0
        fi
        logger -st "SaveLogs" "copying $HOSTNAME logs.tar.gz to USB drive"
    if [[ -d "$UDIR" ]]; then 
           cp /home/Dak/.settings/logs.tar.gz $UDIR/$HOSTNAME.tar.gz
        fi
    if [[ -d "$LDIR" ]]; then 
           cp /home/Dak/.settings/logs.tar.gz $LDIR/$HOSTNAME.tar.gz
        fi
        logger -st "SaveLogs" "Finished"
        return 1
     else
        logger -st "SaveLogs" "No logs directory found on USB drive"
    return 0
     fi
}

case "$1" in
  start)
         logger -st "Recovery USB" "Started"
     SaveLogs
         GetManagementPort
         FindBackupFile $2
         if [ $? = 1 ]; then
             LoadBackupFromFile "$FILE"
             if [ $? = 0 ]; then
                 logger -st "Recovery USB" "Restore Successful!"
                 /etc/init.d/ledcio restart 0.1
             fi
             umount $2
         else
             # No config/ttable backup on this USB. Treat it as a possible
             # password-reset stick so each USB is single-purpose per boot
             # (prevents a stale reset marker on a config USB from unintentionally
             # resetting the password).
             if [ -e "$2/vip5060/reset-password" ]; then
                 RestoreFactoryPassword "$2"
                 umount "$2"
             else
                 /etc/init.d/ledcio restart
             fi
         fi		
         logger -st "Recovery USB" "Finished"
     ;;
  stop)
    ;;
  restart|reload|force-reload)
    ;;
  *)
    echo "Usage: /etc/init.d/$NAME {start}"
    exit 1
    ;;
esac

exit 0
