Files
FlipperDroid/system/bin/flipperdroid-webui

366 lines
12 KiB
Plaintext
Raw Normal View History

#!/system/bin/sh
# FlipperDroid WebUI — HTTP server with bridge API endpoints
WEBROOT="/data/adb/modules/flipperdroid/webroot"
CONFIG_DIR="/data/adb/flipperdroid"
CONFIG_FILE="$CONFIG_DIR/config.sh"
CMD_FIFO="$CONFIG_DIR/cmd_fifo"
RESP_DIR="$CONFIG_DIR/responses"
PORT=8089
mkdir -p "$CONFIG_DIR" "$RESP_DIR"
source "$CONFIG_FILE" 2>/dev/null
PORT="${WEBUI_PORT:-$PORT}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [webui] $1"; }
# Generate unique request ID
req_id() { echo "req_$(date +%s%N | cut -c1-16)_$$"; }
# Send command to bridge daemon via FIFO and wait for response
bridge_cmd() {
local subsystem="$1"
local action="$2"
local params="${3:-}"
local rid=$(req_id)
local timeout=10
echo "${rid}|${subsystem}|${action}|${params}" > "$CMD_FIFO" 2>/dev/null
# Wait for response file
local waited=0
while [ ! -f "$RESP_DIR/${rid}" ] && [ $waited -lt $timeout ]; do
sleep 0.1
waited=$((waited + 1))
done
if [ -f "$RESP_DIR/${rid}" ]; then
cat "$RESP_DIR/${rid}"
rm -f "$RESP_DIR/${rid}"
else
echo "ERR:timeout"
fi
}
# Read config as JSON
config_json() {
source "$CONFIG_FILE" 2>/dev/null
local status=$(cat "$CONFIG_DIR/status" 2>/dev/null || echo "unknown")
local conn_type=$(cat "$CONFIG_DIR/conn_type" 2>/dev/null || echo "none")
local flipper_dev=$(cat "$CONFIG_DIR/flipper_dev" 2>/dev/null || echo "")
local flipper_product=$(cat "$CONFIG_DIR/flipper_product" 2>/dev/null || echo "")
local flipper_serial=$(cat "$CONFIG_DIR/flipper_serial" 2>/dev/null || echo "")
local flipper_version=$(cat "$CONFIG_DIR/flipper_version" 2>/dev/null || echo "")
local flipper_caps=$(cat "$CONFIG_DIR/flipper_caps" 2>/dev/null || echo "")
cat << EOF
{
"status": "$status",
"conn_type": "$conn_type",
"conn_mode": "${CONN_MODE:-auto}",
"device": "$flipper_dev",
"product": "$flipper_product",
"serial": "$flipper_serial",
"firmware_version": "$flipper_version",
"capabilities": "$flipper_caps",
"config": {
"auto_connect": ${AUTO_CONNECT:-1},
"baud_rate": ${BAUD_RATE:-115200},
"webui_port": ${WEBUI_PORT:-8089},
"enable_gpio": ${ENABLE_GPIO:-1},
"enable_subghz": ${ENABLE_SUBGHZ:-1},
"enable_rfid": ${ENABLE_RFID:-1},
"enable_nfc": ${ENABLE_NFC:-1},
"enable_ir": ${ENABLE_IR:-1},
"enable_ibutton": ${ENABLE_IBUTTON:-1},
"enable_badusb": ${ENABLE_BADUSB:-0},
"cpu_share": ${CPU_SHARE:-1},
"cpu_share_threads": ${CPU_SHARE_THREADS:-2},
"log_level": ${LOG_LEVEL:-2}
}
}
EOF
}
# Get recent events as JSON array
events_json() {
local count="${1:-20}"
local events_file="$CONFIG_DIR/events"
[ ! -f "$events_file" ] && echo "[]" && return
echo -n "["
local first=1
tail -n "$count" "$events_file" | while IFS='|' read -r type data1 data2 ts; do
[ $first -eq 1 ] && first=0 || echo -n ","
echo -n "{\"type\":\"$type\",\"data\":\"$data1\",\"extra\":\"$data2\",\"timestamp\":$ts}"
done
echo "]"
}
#############################
# HTTP request handler
#############################
handle_request() {
local method=""
local path=""
local content_length=0
local body=""
# Read request line
read -r line
method=$(echo "$line" | awk '{print $1}')
path=$(echo "$line" | awk '{print $2}')
# Read headers
while read -r header; do
header=$(echo "$header" | tr -d '\r')
[ -z "$header" ] && break
case "$header" in
Content-Length:*|content-length:*)
content_length=$(echo "$header" | awk '{print $2}')
;;
esac
done
# Read body if POST
if [ "$method" = "POST" ] && [ "$content_length" -gt 0 ] 2>/dev/null; then
body=$(dd bs=1 count="$content_length" 2>/dev/null)
fi
# Route
local response=""
local content_type="application/json"
local status="200 OK"
case "$method $path" in
# Static files
"GET /"|"GET /index.html")
content_type="text/html"
response=$(cat "$WEBROOT/index.html" 2>/dev/null)
;;
"GET /css/style.css")
content_type="text/css"
response=$(cat "$WEBROOT/css/style.css" 2>/dev/null)
;;
"GET /js/app.js")
content_type="application/javascript"
response=$(cat "$WEBROOT/js/app.js" 2>/dev/null)
;;
# API: Status & Config
"GET /api/status")
response=$(config_json)
;;
"GET /api/events"*)
response=$(events_json 50)
;;
"GET /api/log")
content_type="text/plain"
response=$(tail -n 100 "$CONFIG_DIR/logs/flipperdroid.log" 2>/dev/null)
;;
# API: System commands
"POST /api/system/ping")
response="{\"result\":\"$(bridge_cmd system ping)\"}"
;;
"POST /api/system/version")
response="{\"result\":\"$(bridge_cmd system version)\"}"
;;
"POST /api/system/status")
response="{\"result\":\"$(bridge_cmd system status)\"}"
;;
# API: GPIO
"POST /api/gpio/init")
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
local mode=$(echo "$body" | grep -o '"mode":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd gpio init "$pin,$mode")\"}"
;;
"POST /api/gpio/write")
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
local val=$(echo "$body" | grep -o '"value":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd gpio write "$pin,$val")\"}"
;;
"POST /api/gpio/read")
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd gpio read "$pin")\"}"
;;
"POST /api/gpio/pwm")
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
local freq=$(echo "$body" | grep -o '"freq":[0-9]*' | grep -o '[0-9]*')
local duty=$(echo "$body" | grep -o '"duty":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd gpio pwm "$pin,$freq,$duty")\"}"
;;
"POST /api/gpio/adc")
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd gpio adc "$pin")\"}"
;;
# API: SubGHz
"POST /api/subghz/set_freq")
local freq=$(echo "$body" | grep -o '"freq":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd subghz set_freq "$freq")\"}"
;;
"POST /api/subghz/tx")
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
response="{\"result\":\"$(bridge_cmd subghz tx "$data")\"}"
;;
"POST /api/subghz/rx_start")
response="{\"result\":\"$(bridge_cmd subghz rx_start)\"}"
;;
"POST /api/subghz/rx_stop")
response="{\"result\":\"$(bridge_cmd subghz rx_stop)\"}"
;;
"POST /api/subghz/get_rssi")
response="{\"result\":\"$(bridge_cmd subghz get_rssi)\"}"
;;
"POST /api/subghz/replay")
local slot=$(echo "$body" | grep -o '"slot":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd subghz replay "$slot")\"}"
;;
# API: RFID
"POST /api/rfid/read")
response="{\"result\":\"$(bridge_cmd rfid read)\"}"
;;
"POST /api/rfid/emulate")
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
response="{\"result\":\"$(bridge_cmd rfid emulate "$data")\"}"
;;
# API: NFC
"POST /api/nfc/poll")
response="{\"result\":\"$(bridge_cmd nfc poll)\"}"
;;
"POST /api/nfc/read_full")
response="{\"result\":\"$(bridge_cmd nfc read_full)\"}"
;;
"POST /api/nfc/emulate")
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
local type=$(echo "$body" | grep -o '"type":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd nfc emulate "${data}${type}")\"}"
;;
"POST /api/nfc/relay_start")
response="{\"result\":\"$(bridge_cmd nfc relay_start)\"}"
;;
"POST /api/nfc/relay_stop")
response="{\"result\":\"$(bridge_cmd nfc relay_stop)\"}"
;;
# API: IR
"POST /api/ir/tx")
local proto=$(echo "$body" | grep -o '"protocol":[0-9]*' | grep -o '[0-9]*')
local addr=$(echo "$body" | grep -o '"address":[0-9]*' | grep -o '[0-9]*')
local cmd=$(echo "$body" | grep -o '"command":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd ir tx "$proto,$addr,$cmd")\"}"
;;
"POST /api/ir/rx_start")
response="{\"result\":\"$(bridge_cmd ir rx_start)\"}"
;;
"POST /api/ir/rx_stop")
response="{\"result\":\"$(bridge_cmd ir rx_stop)\"}"
;;
"POST /api/ir/replay")
local slot=$(echo "$body" | grep -o '"slot":[0-9]*' | grep -o '[0-9]*')
response="{\"result\":\"$(bridge_cmd ir replay "$slot")\"}"
;;
# API: iButton
"POST /api/ibutton/read")
response="{\"result\":\"$(bridge_cmd ibutton read)\"}"
;;
"POST /api/ibutton/emulate")
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
response="{\"result\":\"$(bridge_cmd ibutton emulate "$data")\"}"
;;
# API: BadUSB
"POST /api/badusb/start")
response="{\"result\":\"$(bridge_cmd badusb start)\"}"
;;
"POST /api/badusb/exec")
local script=$(echo "$body" | grep -o '"script":"[^"]*"' | cut -d'"' -f4)
response="{\"result\":\"$(bridge_cmd badusb exec "$script")\"}"
;;
"POST /api/badusb/stop")
response="{\"result\":\"$(bridge_cmd badusb stop)\"}"
;;
# API: File operations on Flipper SD
"POST /api/file/list")
local fpath=$(echo "$body" | grep -o '"path":"[^"]*"' | cut -d'"' -f4)
response="{\"result\":\"$(bridge_cmd file list "$fpath")\"}"
;;
# API: Config update
"POST /api/config")
# Write config values back
local key=$(echo "$body" | grep -o '"key":"[^"]*"' | cut -d'"' -f4)
local value=$(echo "$body" | grep -o '"value":"[^"]*"' | cut -d'"' -f4)
if [ -n "$key" ] && [ -n "$value" ]; then
sed -i "s/^${key}=.*/${key}=${value}/" "$CONFIG_FILE"
source "$CONFIG_FILE"
response="{\"ok\":true}"
else
response="{\"ok\":false,\"error\":\"missing key or value\"}"
fi
;;
# API: Reconnect
"POST /api/reconnect")
echo "disconnected" > "$CONFIG_DIR/status"
response="{\"ok\":true,\"message\":\"reconnecting...\"}"
;;
# API: Stealth
"GET /api/stealth/status")
response=$(/system/bin/fd-stealth status 2>/dev/null || echo '{"error":"stealth not available"}')
;;
"POST /api/stealth/apply")
/system/bin/fd-stealth apply >> "$CONFIG_DIR/logs/stealth.log" 2>&1
response="{\"ok\":true,\"message\":\"stealth applied\"}"
;;
"POST /api/stealth/teardown")
/system/bin/fd-stealth teardown >> "$CONFIG_DIR/logs/stealth.log" 2>&1
response="{\"ok\":true,\"message\":\"stealth removed\"}"
;;
"POST /api/stealth/hide")
/system/bin/fd-stealth hide-dev >> "$CONFIG_DIR/logs/stealth.log" 2>&1
response="{\"ok\":true,\"message\":\"device hidden\"}"
;;
"POST /api/stealth/show")
/system/bin/fd-stealth show-dev >> "$CONFIG_DIR/logs/stealth.log" 2>&1
response="{\"ok\":true,\"message\":\"device visible\"}"
;;
*)
status="404 Not Found"
response="{\"error\":\"not found\"}"
;;
esac
# Send HTTP response
local content_length=${#response}
printf "HTTP/1.1 %s\r\n" "$status"
printf "Content-Type: %s\r\n" "$content_type"
printf "Content-Length: %d\r\n" "$content_length"
printf "Access-Control-Allow-Origin: *\r\n"
printf "Connection: close\r\n"
printf "\r\n"
printf "%s" "$response"
}
#############################
# Start HTTP server
#############################
log "FlipperDroid WebUI starting on port $PORT"
# Use busybox nc or toybox for HTTP serving
while true; do
handle_request | busybox nc -l -p "$PORT" -w 30 2>/dev/null || \
handle_request | toybox nc -l -p "$PORT" -W 30 2>/dev/null || \
handle_request | nc -l -p "$PORT" 2>/dev/null
done