#!/bin/bash
PLUGIN="unraid-management-agent"
CONFIG_FILE="/boot/config/plugins/$PLUGIN/config.cfg"
PROG="/usr/local/emhttp/plugins/$PLUGIN/$PLUGIN"
LOGS_DIR="/var/log"

# Create config directory if it doesn't exist
mkdir -p "/boot/config/plugins/$PLUGIN"

# Create default config file if it doesn't exist
if [ ! -f "$CONFIG_FILE" ]; then
    cat > "$CONFIG_FILE" << 'EOF'
PORT="8043"
EOF
fi

# Read configuration
source "$CONFIG_FILE"

# Stop if it's already running
killall "$PLUGIN" >/dev/null 2>&1

# ── Input validation helpers ──────────────────────────────────
# Strip anything that isn't a digit
sanitize_int() { echo "$1" | tr -cd '0-9'; }
# Strip anything that isn't alphanumeric, comma, or hyphen
sanitize_csv() { echo "$1" | tr -cd 'a-zA-Z0-9,_-'; }
# Strip shell metacharacters from freeform strings
sanitize_str() { echo "$1" | tr -d "'\"\`\$\\"; }
# Validate boolean: only "true" passes, everything else is "false"
sanitize_bool() { [ "$1" = "true" ] && echo "true" || echo "false"; }

# ── Set defaults ──────────────────────────────────────────────
PORT="${PORT:-8043}"
BIND_ADDRESS="${BIND_ADDRESS:-}"
READ_ONLY="${READ_ONLY:-false}"
API_TOKEN="${API_TOKEN:-}"
LOG_LEVEL="${LOG_LEVEL:-info}"
# Defaults follow industry standards (Zabbix, Prometheus, Datadog)
INTERVAL_SYSTEM="${INTERVAL_SYSTEM:-15}"
INTERVAL_ARRAY="${INTERVAL_ARRAY:-60}"
INTERVAL_DISK="${INTERVAL_DISK:-300}"
INTERVAL_DOCKER="${INTERVAL_DOCKER:-30}"
INTERVAL_VM="${INTERVAL_VM:-60}"
INTERVAL_UPS="${INTERVAL_UPS:-60}"
INTERVAL_NUT="${INTERVAL_NUT:-0}"
INTERVAL_GPU="${INTERVAL_GPU:-60}"
INTERVAL_SHARES="${INTERVAL_SHARES:-60}"
INTERVAL_NETWORK="${INTERVAL_NETWORK:-60}"
INTERVAL_HARDWARE="${INTERVAL_HARDWARE:-600}"
INTERVAL_ZFS="${INTERVAL_ZFS:-0}"
INTERVAL_NOTIFICATION="${INTERVAL_NOTIFICATION:-30}"
INTERVAL_REGISTRATION="${INTERVAL_REGISTRATION:-600}"
INTERVAL_UNASSIGNED="${INTERVAL_UNASSIGNED:-60}"

# MQTT settings (optional, disabled by default)
MQTT_ENABLED="${MQTT_ENABLED:-false}"
MQTT_BROKER="${MQTT_BROKER:-}"
MQTT_PORT="${MQTT_PORT:-1883}"
MQTT_USERNAME="${MQTT_USERNAME:-}"
MQTT_PASSWORD="${MQTT_PASSWORD:-}"
MQTT_CLIENT_ID="${MQTT_CLIENT_ID:-unraid-management-agent}"
MQTT_TOPIC_PREFIX="${MQTT_TOPIC_PREFIX:-unraid}"
MQTT_HOME_ASSISTANT="${MQTT_HOME_ASSISTANT:-false}"

# ── Sanitize all config values ────────────────────────────────
PORT=$(sanitize_int "$PORT");                       PORT="${PORT:-8043}"
# Bind address: keep only IP characters (hex digits, dots, colons).
# The daemon validates the value and falls back to all interfaces if invalid.
BIND_ADDRESS=$(echo "$BIND_ADDRESS" | tr -cd '0-9a-fA-F.:')
READ_ONLY=$(sanitize_bool "$READ_ONLY")
LOG_LEVEL=$(echo "$LOG_LEVEL" | grep -xE 'debug|info|warning|error' || echo "info")
LOG_LEVEL="${LOG_LEVEL:-info}"

for var in INTERVAL_SYSTEM INTERVAL_ARRAY INTERVAL_DISK INTERVAL_DOCKER \
           INTERVAL_VM INTERVAL_UPS INTERVAL_NUT INTERVAL_GPU INTERVAL_SHARES \
           INTERVAL_NETWORK INTERVAL_HARDWARE INTERVAL_ZFS INTERVAL_UNASSIGNED \
           INTERVAL_NOTIFICATION INTERVAL_REGISTRATION; do
    eval "$var=\$(sanitize_int \"\$$var\")"
done

for var in MQTT_ENABLED MQTT_HOME_ASSISTANT; do
    eval "$var=\$(sanitize_bool \"\$$var\")"
done
for var in MQTT_BROKER MQTT_USERNAME MQTT_PASSWORD MQTT_CLIENT_ID MQTT_TOPIC_PREFIX; do
    eval "$var=\$(sanitize_str \"\$$var\")"
done

# API_TOKEN is deliberately NOT passed through sanitize_str. Stripping shell
# metacharacters would silently alter the credential, and the resulting 401s
# give no hint as to why. The value is handed to env(1) as a single quoted
# argument, so its contents are never interpreted by the shell.

# `source` above expands $, `, and \ inside a double-quoted value, so a token
# written as API_TOKEN="tok$en" arrives here truncated — or empty, which would
# leave authentication silently disabled on a server the operator believes is
# protected. Compare against the raw file value and refuse to start on any
# mismatch; single-quoting the token in config.cfg avoids this entirely.
raw_api_token=$(sed -n 's/^[[:space:]]*API_TOKEN=//p' "$CONFIG_FILE" | tail -n1)
# Remove at most one matching wrapper, chosen by the first character. Stripping
# both quote styles in sequence would turn API_TOKEN="'token'" into token while
# the shell yields 'token', refusing a perfectly valid config.
case "$raw_api_token" in
    '"'*'"')
        raw_api_token=${raw_api_token#\"}
        raw_api_token=${raw_api_token%\"}
        ;;
    "'"*"'")
        raw_api_token=${raw_api_token#\'}
        raw_api_token=${raw_api_token%\'}
        ;;
esac
if [ "$raw_api_token" != "$API_TOKEN" ]; then
    echo "Error: API_TOKEN in $CONFIG_FILE was altered by shell expansion." >&2
    echo "       Single-quote it, e.g. API_TOKEN='your-token'." >&2
    echo "       If the token contains a literal single quote, double-quote it" >&2
    echo "       instead; if it also contains \$, \` or \\, set the API_TOKEN" >&2
    echo "       environment variable rather than config.cfg." >&2
    exit 1
fi

# Reject control characters, which cannot appear in an HTTP header value anyway.
# tr, not grep: grep is line-oriented and never sees an embedded newline as a
# character within a line, so it silently accepts the most likely stray byte.
if [ -n "$API_TOKEN" ] && [ "$(printf '%s' "$API_TOKEN" | tr -d '[:cntrl:]')" != "$API_TOKEN" ]; then
    echo "Error: API_TOKEN contains control characters; refusing to start" >&2
    exit 1
fi
MQTT_PORT=$(sanitize_int "$MQTT_PORT");             MQTT_PORT="${MQTT_PORT:-1883}"

# ── Launch using env(1) to pass variables safely ──────────────
nohup env \
  BIND_ADDRESS="$BIND_ADDRESS" \
  READ_ONLY="$READ_ONLY" \
  API_TOKEN="$API_TOKEN" \
  INTERVAL_SYSTEM="$INTERVAL_SYSTEM" \
  INTERVAL_ARRAY="$INTERVAL_ARRAY" \
  INTERVAL_DISK="$INTERVAL_DISK" \
  INTERVAL_DOCKER="$INTERVAL_DOCKER" \
  INTERVAL_VM="$INTERVAL_VM" \
  INTERVAL_UPS="$INTERVAL_UPS" \
  INTERVAL_NUT="$INTERVAL_NUT" \
  INTERVAL_GPU="$INTERVAL_GPU" \
  INTERVAL_SHARES="$INTERVAL_SHARES" \
  INTERVAL_NETWORK="$INTERVAL_NETWORK" \
  INTERVAL_HARDWARE="$INTERVAL_HARDWARE" \
  INTERVAL_ZFS="$INTERVAL_ZFS" \
  INTERVAL_NOTIFICATION="$INTERVAL_NOTIFICATION" \
  INTERVAL_REGISTRATION="$INTERVAL_REGISTRATION" \
  INTERVAL_UNASSIGNED="$INTERVAL_UNASSIGNED" \
  MQTT_ENABLED="$MQTT_ENABLED" \
  MQTT_BROKER="$MQTT_BROKER" \
  MQTT_PORT="$MQTT_PORT" \
  MQTT_USERNAME="$MQTT_USERNAME" \
  MQTT_PASSWORD="$MQTT_PASSWORD" \
  MQTT_CLIENT_ID="$MQTT_CLIENT_ID" \
  MQTT_TOPIC_PREFIX="$MQTT_TOPIC_PREFIX" \
  MQTT_HOME_ASSISTANT="$MQTT_HOME_ASSISTANT" \
  "$PROG" --logs-dir "$LOGS_DIR" --port "$PORT" --log-level "$LOG_LEVEL" boot \
  >/dev/null 2>&1 &

echo "$PLUGIN started on port $PORT"
