#!/bin/sh
# Command dispatcher for CI test containers.
#
# Supported commands:
#   uci show|get|set|commit|delete|add  <args...>
#   pidof <name>
#   sighup <name>
#   cat <absolute-path>
#   ubus call <object> <method>
#   writefile <absolute-path> <base64-content>
#
# Argument constraints:
#   - No argument may contain whitespace
#   - uci/ubus tokens: [a-zA-Z0-9_.@[]=/:,+-]
#   - process names:   [a-zA-Z0-9_-]
#   - file paths:      absolute, no "..", [a-zA-Z0-9_/.-]
#   - writefile content: standard base64 alphabet [A-Za-z0-9+/=]

set -f
set -e

IFS= read -r line || exit 0
[ -z "$line" ] && exit 0

# Split into positional parameters on whitespace (glob expansion is off).
# shellcheck disable=SC2086
set -- $line
cmd="$1"; shift

_validate_name() {
    # Accepts: process names, uci config names.
    case "$1" in
        ''|*[!a-zA-Z0-9_-]*)
            printf 'ERROR: unsafe name: %s\n' "$1" >&2; exit 1 ;;
    esac
}

_validate_token() {
    # Accepts: uci key-value args (may include @, [, ], ., =, :, /).
    [ -z "$1" ] && { printf 'ERROR: empty token\n' >&2; exit 1; }
    if ! printf '%s' "$1" | grep -qE '^[a-zA-Z0-9_.@\[\]=/:,+\-]+$'; then
        printf 'ERROR: unsafe token: %s\n' "$1" >&2; exit 1
    fi
}

_validate_path() {
    # Must be absolute, no ".." characters.
    case "$1" in
        /*) : ;;
        *) printf 'ERROR: path must be absolute: %s\n' "$1" >&2; exit 1 ;;
    esac
    case "$1" in
        *..*) printf 'ERROR: path traversal not allowed: %s\n' "$1" >&2; exit 1 ;;
    esac
    if ! printf '%s' "$1" | grep -qE '^[a-zA-Z0-9_/.\-]+$'; then
        printf 'ERROR: unsafe path character: %s\n' "$1" >&2; exit 1
    fi
}

case "$cmd" in
    uci)
        subcmd="$1"; shift
        case "$subcmd" in
            show|get|set|commit|delete|add)
                for arg; do _validate_token "$arg"; done
                uci "$subcmd" "$@"
                ;;
            *)
                printf 'ERROR: uci subcommand not allowed: %s\n' "$subcmd" >&2
                exit 1 ;;
        esac
        ;;

    pidof)
        [ $# -eq 1 ] || { printf 'ERROR: pidof requires exactly 1 argument\n' >&2; exit 1; }
        _validate_name "$1"
        pidof "$1"
        ;;

    sighup)
	[ $# -eq 1 ] || { printf 'ERROR: sighup requires exactly 1 argument\n' >&2; exit 1; }
        _validate_name "$1"
        pid=$(pidof "$1") || true
        [ -n "$pid" ] && kill -HUP $pid
        ;;

    cat)
        [ $# -eq 1 ] || { printf 'ERROR: cat requires exactly 1 argument\n' >&2; exit 1; }
        _validate_path "$1"
        cat "$1"
        ;;

    ubus)
        subcmd="$1"; shift
        [ "$subcmd" = "call" ] || {
            printf 'ERROR: ubus subcommand not allowed: %s\n' "$subcmd" >&2
            exit 1
        }
        for arg; do _validate_token "$arg"; done
        ubus call "$@"
        ;;

    writefile)
        # writefile <absolute-path> <base64-encoded-content>
        # Content is base64-encoded by the caller so that arbitrary file data
        # can be transferred safely over the single-line text protocol.
        [ $# -eq 2 ] || {
            printf 'ERROR: writefile requires exactly 2 arguments\n' >&2; exit 1
        }
        _validate_path "$1"
        case "$2" in
            ''|*[!A-Za-z0-9+/=]*)
                printf 'ERROR: invalid base64 content\n' >&2; exit 1 ;;
        esac
        tmpfile=$(mktemp) || exit 1
        if printf '%s' "$2" | base64 -d > "$tmpfile"; then
            mv "$tmpfile" "$1"
        else
            rm -f "$tmpfile"
            printf 'ERROR: base64 decode failed\n' >&2
            exit 1
        fi
        ;;

    *)
        printf 'ERROR: command not allowed: %s\n' "$cmd" >&2
        exit 1 ;;
esac
