#!/bin/sh
# SecuBox Tor Shield - CLI management tool
# Copyright (C) 2025 CyberMind.fr

CONFIG="tor-shield"
TOR_CONTROL="/var/run/tor/control"
TOR_DATA="/var/lib/tor"

. /lib/functions.sh

usage() {
	cat << EOF
Usage: torctl <command> [options]

Commands:
  status          Show Tor Shield status
  enable [preset] Enable Tor Shield (presets: anonymous, selective, censored, server)
  disable         Disable Tor Shield
  restart         Restart Tor Shield
  identity        Get new Tor identity (new circuits)
  circuits        Show active circuits
  exit-ip         Show current exit IP address
  leak-test       Test for DNS/IP leaks
  bridges         Manage bridge configuration
  hidden          Manage hidden services

Options:
  -h, --help      Show this help

Examples:
  torctl enable anonymous    Enable with full anonymity preset
  torctl enable server       Enable server mode (public IP + Tor outbound)
  torctl status              Show current status
  torctl identity            Request new circuits
  torctl exit-ip             Show Tor exit IP
EOF
}

# Send command to Tor control socket
tor_control() {
	if [ ! -S "$TOR_CONTROL" ]; then
		echo "Error: Tor control socket not available"
		return 1
	fi
	echo -e "$1" | nc -U "$TOR_CONTROL" 2>/dev/null
}

# Get bootstrap percentage
get_bootstrap() {
	local status=$(tor_control "GETINFO status/bootstrap-phase")
	echo "$status" | grep "PROGRESS=" | sed 's/.*PROGRESS=\([0-9]*\).*/\1/'
}

# Check if Tor is running
is_running() {
	pgrep tor >/dev/null 2>&1
}

# Get current exit IP
get_exit_ip() {
	# Try multiple services to get external IP through Tor
	local socks_port=$(uci -q get tor-shield.socks.port || echo "9050")
	local ip=""

	# Try check.torproject.org first
	ip=$(curl -s --socks5-hostname 127.0.0.1:$socks_port https://check.torproject.org/api/ip 2>/dev/null | jsonfilter -e '@.IP' 2>/dev/null)

	if [ -z "$ip" ]; then
		# Fallback to ipinfo.io
		ip=$(curl -s --socks5-hostname 127.0.0.1:$socks_port https://ipinfo.io/ip 2>/dev/null)
	fi

	echo "${ip:-unknown}"
}

# Get real IP (without Tor)
get_real_ip() {
	local ip=$(curl -s --max-time 5 https://ipinfo.io/ip 2>/dev/null)
	echo "${ip:-unknown}"
}

# Status command
cmd_status() {
	local enabled mode bootstrap exit_ip circuit_count

	config_load "$CONFIG"
	config_get enabled main enabled '0'
	config_get mode main mode 'transparent'

	echo "Tor Shield Status"
	echo "================="

	if [ "$enabled" != "1" ]; then
		echo "Status: DISABLED"
		echo ""
		echo "Enable with: torctl enable"
		return 0
	fi

	if ! is_running; then
		echo "Status: ENABLED but NOT RUNNING"
		echo ""
		echo "Start with: /etc/init.d/tor-shield start"
		return 1
	fi

	bootstrap=$(get_bootstrap)
	bootstrap=${bootstrap:-0}

	echo "Status: ACTIVE"
	echo "Mode: $mode"
	echo "Bootstrap: ${bootstrap}%"

	if [ "$bootstrap" -ge 100 ]; then
		exit_ip=$(get_exit_ip)
		echo "Exit IP: $exit_ip"

		# Get circuit count
		circuit_count=$(tor_control "GETINFO circuit-status" | grep -c "BUILT")
		echo "Circuits: ${circuit_count:-0}"
	else
		echo "Exit IP: (bootstrapping...)"
	fi

	# Show config details
	local dns_over_tor kill_switch
	config_get dns_over_tor main dns_over_tor '1'
	config_get kill_switch main kill_switch '1'

	echo ""
	echo "Configuration:"
	echo "  DNS over Tor: $([ "$dns_over_tor" = "1" ] && echo "Yes" || echo "No")"
	echo "  Kill Switch: $([ "$kill_switch" = "1" ] && echo "Yes" || echo "No")"

	# Bridge status
	local bridges_enabled
	config_get bridges_enabled bridges enabled '0'
	if [ "$bridges_enabled" = "1" ]; then
		local bridge_type
		config_get bridge_type bridges type 'obfs4'
		echo "  Bridges: $bridge_type"
	fi
}

# Enable command
cmd_enable() {
	local preset="${1:-anonymous}"

	echo "Enabling Tor Shield with preset: $preset"

	# Load preset configuration
	config_load "$CONFIG"

	local preset_mode preset_dns preset_kill preset_bridges preset_lan_proxy
	config_get preset_mode "$preset" mode 'transparent'
	config_get preset_dns "$preset" dns_over_tor '1'
	config_get preset_kill "$preset" kill_switch '1'
	config_get preset_bridges "$preset" use_bridges '0'
	config_get preset_lan_proxy "$preset" lan_proxy '0'

	# Apply preset settings
	uci set tor-shield.main.enabled='1'
	uci set tor-shield.main.mode="$preset_mode"
	uci set tor-shield.main.dns_over_tor="$preset_dns"
	uci set tor-shield.main.kill_switch="$preset_kill"
	uci set tor-shield.trans.lan_proxy="$preset_lan_proxy"

	if [ "$preset_bridges" = "1" ]; then
		uci set tor-shield.bridges.enabled='1'
	fi

	uci commit tor-shield

	# Start service
	/etc/init.d/tor-shield restart

	echo "Tor Shield enabled. Waiting for bootstrap..."

	# Wait for bootstrap
	local count=0
	while [ $count -lt 60 ]; do
		if is_running; then
			local progress=$(get_bootstrap)
			if [ -n "$progress" ] && [ "$progress" -ge 100 ]; then
				echo "Bootstrap complete!"
				cmd_status
				return 0
			fi
			echo "Bootstrap: ${progress:-0}%"
		fi
		sleep 2
		count=$((count + 1))
	done

	echo "Warning: Bootstrap taking longer than expected"
	cmd_status
}

# Disable command
cmd_disable() {
	echo "Disabling Tor Shield..."

	uci set tor-shield.main.enabled='0'
	uci commit tor-shield

	/etc/init.d/tor-shield stop

	echo "Tor Shield disabled."
}

# Restart command
cmd_restart() {
	echo "Restarting Tor Shield..."
	/etc/init.d/tor-shield restart
	sleep 2
	cmd_status
}

# New identity command
cmd_identity() {
	if ! is_running; then
		echo "Error: Tor is not running"
		return 1
	fi

	echo "Requesting new identity..."

	# Send NEWNYM signal
	local result=$(tor_control "SIGNAL NEWNYM")

	if echo "$result" | grep -q "250 OK"; then
		echo "New identity requested successfully."
		echo "New circuits will be established shortly."
		sleep 3
		echo ""
		echo "New exit IP: $(get_exit_ip)"
	else
		echo "Failed to request new identity"
		return 1
	fi
}

# Circuits command
cmd_circuits() {
	if ! is_running; then
		echo "Error: Tor is not running"
		return 1
	fi

	echo "Active Circuits"
	echo "==============="

	local circuits=$(tor_control "GETINFO circuit-status")

	echo "$circuits" | grep "BUILT" | while read line; do
		# Parse circuit info
		local id=$(echo "$line" | awk '{print $1}')
		local status=$(echo "$line" | awk '{print $2}')
		local path=$(echo "$line" | awk '{print $3}')

		if [ -n "$path" ]; then
			# Extract node names/fingerprints
			echo "Circuit $id: $path"
		fi
	done

	# Get circuit count
	local count=$(echo "$circuits" | grep -c "BUILT")
	echo ""
	echo "Total built circuits: ${count:-0}"
}

# Exit IP command
cmd_exit_ip() {
	if ! is_running; then
		echo "Error: Tor is not running"
		return 1
	fi

	local bootstrap=$(get_bootstrap)
	if [ -z "$bootstrap" ] || [ "$bootstrap" -lt 100 ]; then
		echo "Tor is still bootstrapping (${bootstrap:-0}%)"
		return 1
	fi

	local exit_ip=$(get_exit_ip)
	local real_ip=$(get_real_ip)

	echo "Real IP: $real_ip"
	echo "Tor Exit IP: $exit_ip"

	if [ "$exit_ip" != "$real_ip" ] && [ "$exit_ip" != "unknown" ]; then
		echo "Status: PROTECTED"
	else
		echo "Status: WARNING - IPs match or unknown"
	fi
}

# Leak test command
cmd_leak_test() {
	if ! is_running; then
		echo "Error: Tor is not running"
		return 1
	fi

	echo "Running leak test..."
	echo ""

	local socks_port=$(uci -q get tor-shield.socks.port || echo "9050")
	local leaks=0

	# Test 1: IP leak
	echo "1. IP Leak Test"
	local tor_ip=$(get_exit_ip)
	local real_ip=$(get_real_ip)

	if [ "$tor_ip" = "$real_ip" ] || [ "$tor_ip" = "unknown" ]; then
		echo "   FAIL: IP may be leaking"
		leaks=$((leaks + 1))
	else
		echo "   PASS: Tor IP ($tor_ip) differs from real IP ($real_ip)"
	fi

	# Test 2: DNS leak (via Tor check)
	echo ""
	echo "2. Tor Detection Test"
	local tor_check=$(curl -s --socks5-hostname 127.0.0.1:$socks_port https://check.torproject.org/api/ip 2>/dev/null)
	local is_tor=$(echo "$tor_check" | jsonfilter -e '@.IsTor' 2>/dev/null)

	if [ "$is_tor" = "true" ]; then
		echo "   PASS: Traffic confirmed going through Tor"
	else
		echo "   FAIL: Traffic may not be going through Tor"
		leaks=$((leaks + 1))
	fi

	# Test 3: DNS resolution through Tor
	echo ""
	echo "3. DNS Over Tor Test"
	local dns_over_tor=$(uci -q get tor-shield.main.dns_over_tor)
	if [ "$dns_over_tor" = "1" ]; then
		# Try resolving a .onion address (only works through Tor)
		local dns_test=$(curl -s --socks5-hostname 127.0.0.1:$socks_port http://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ 2>/dev/null | head -c 100)
		if [ -n "$dns_test" ]; then
			echo "   PASS: DNS resolution working through Tor"
		else
			echo "   WARNING: Could not verify DNS over Tor"
		fi
	else
		echo "   SKIP: DNS over Tor is disabled"
	fi

	echo ""
	echo "===================="
	if [ $leaks -eq 0 ]; then
		echo "Result: ALL TESTS PASSED"
	else
		echo "Result: $leaks POTENTIAL LEAK(S) DETECTED"
	fi
}

# Bridges command
cmd_bridges() {
	local action="${1:-status}"

	case "$action" in
		status)
			local enabled type
			config_load "$CONFIG"
			config_get enabled bridges enabled '0'
			config_get type bridges type 'obfs4'

			echo "Bridge Configuration"
			echo "===================="
			echo "Enabled: $([ "$enabled" = "1" ] && echo "Yes" || echo "No")"
			echo "Type: $type"
			echo ""
			echo "Bridge lines:"
			config_list_foreach bridges bridge_lines echo_bridge
			;;
		enable)
			uci set tor-shield.bridges.enabled='1'
			uci commit tor-shield
			echo "Bridges enabled. Restart Tor Shield to apply."
			;;
		disable)
			uci set tor-shield.bridges.enabled='0'
			uci commit tor-shield
			echo "Bridges disabled. Restart Tor Shield to apply."
			;;
		add)
			shift
			local bridge_line="$*"
			if [ -n "$bridge_line" ]; then
				uci add_list tor-shield.bridges.bridge_lines="$bridge_line"
				uci commit tor-shield
				echo "Bridge added. Restart Tor Shield to apply."
			else
				echo "Usage: torctl bridges add <bridge_line>"
			fi
			;;
		*)
			echo "Usage: torctl bridges [status|enable|disable|add <line>]"
			;;
	esac
}

echo_bridge() {
	echo "  $1"
}

# Hidden services command
cmd_hidden() {
	local action="${1:-list}"

	case "$action" in
		list)
			echo "Hidden Services"
			echo "==============="
			config_load "$CONFIG"
			config_foreach list_hidden_service hidden_service
			;;
		add)
			shift
			local name="$1"
			local local_port="${2:-80}"
			local virtual_port="${3:-80}"

			if [ -z "$name" ]; then
				echo "Usage: torctl hidden add <name> [local_port] [virtual_port]"
				return 1
			fi

			# Create new hidden service config
			uci set tor-shield.hs_$name=hidden_service
			uci set tor-shield.hs_$name.name="$name"
			uci set tor-shield.hs_$name.enabled='1'
			uci set tor-shield.hs_$name.local_port="$local_port"
			uci set tor-shield.hs_$name.virtual_port="$virtual_port"
			uci commit tor-shield

			echo "Hidden service '$name' created."
			echo "Restart Tor Shield to generate .onion address."
			;;
		remove)
			local name="$2"
			if [ -z "$name" ]; then
				echo "Usage: torctl hidden remove <name>"
				return 1
			fi

			uci delete tor-shield.hs_$name 2>/dev/null
			uci commit tor-shield

			echo "Hidden service '$name' removed."
			;;
		*)
			echo "Usage: torctl hidden [list|add|remove]"
			;;
	esac
}

list_hidden_service() {
	local cfg="$1"
	local enabled name local_port virtual_port

	config_get enabled "$cfg" enabled '0'
	config_get name "$cfg" name "$cfg"
	config_get local_port "$cfg" local_port '80'
	config_get virtual_port "$cfg" virtual_port '80'

	local status="disabled"
	[ "$enabled" = "1" ] && status="enabled"

	local hostname_file="$TOR_DATA/hidden_service_$name/hostname"
	local onion_addr="(not generated)"
	if [ -f "$hostname_file" ]; then
		onion_addr=$(cat "$hostname_file")
	fi

	echo ""
	echo "Service: $name ($status)"
	echo "  Address: $onion_addr"
	echo "  Port: $virtual_port -> 127.0.0.1:$local_port"
}

# Main dispatcher
case "$1" in
	status)
		cmd_status
		;;
	enable)
		shift
		cmd_enable "$@"
		;;
	disable)
		cmd_disable
		;;
	restart)
		cmd_restart
		;;
	identity|new-identity|newnym)
		cmd_identity
		;;
	circuits|circuit)
		cmd_circuits
		;;
	exit-ip|ip)
		cmd_exit_ip
		;;
	leak-test|leaks|test)
		cmd_leak_test
		;;
	bridges|bridge)
		shift
		cmd_bridges "$@"
		;;
	hidden|hs|onion)
		shift
		cmd_hidden "$@"
		;;
	-h|--help|help)
		usage
		;;
	*)
		usage
		exit 1
		;;
esac
