#!/bin/sh

RULANG=$(uci -q get luci.languages.ru | grep -i russian)
if [ -n "$RULANG" ]; then
	MSG_USB="USB устройства не подключены к роутеру!"
	MSG_USB_HELP="Подождите 30 секунд, запустите диагностику еще раз. Проверьте подключение устройства к роутеру. Проверьте кабель и все соединения. Попробуйте подключить любое заведомо исправное устройство в USB порт роутера, подождите 30 секунд, запустите диагностику еще раз. Если порт роутера исправен, диагностика покажет найденое устройство."
	MSG_MODEM_ON="Автомодем: включен"
	MSG_MODEM_OFF="Автомодем: выключен"
	MSG_ENABLED="включен"
	MSG_DISABLED="выключен"
	MSG_USBDEVFOUND="Найденые USB устройства:"
	MSG_MODEMFOUND="Найден модем:"
	MSG_MODEMCANDIDATE="МОДЕМ?:"
	MSG_FOUNDDEVICES="Найденые устройства подключеные в /dev:"
	MSG_AVAILABLE="доcтупен"
	MSG_ALL="все"
	MSG_BANDS="Диапазоны"
	MSG_LIMITED="ограничено"
	MSG_INTMODEM="Интерфейс модема:"
	MSG_SET="задан"
	MSG_NOTSETAPN="не задан!"
	MSG_NOTSET="не настроен!"
	MSG_PROTO="Протокол"
	MSG_USE="Используется"
	MSG_YES="да"
	MSG_NO="нет"
	MSG_TIMEOUT="Таймаут"
	MSG_USBETHFOUND="Найденые USB сетевые интерфейсы"
	MSG_ROUTING="Роутинг"
	MSG_ROUTINGDEF="маршрут установлен по умолчанию"
	MSG_ROUTINGBACKUP="в резерве (есть более приоритетное подключение)"
	MSG_ROUTINGNOTDEF="маршрут существует, но не установлен по умолчанию"
	MSG_ROUTINGFAIL="маршрут отсутствует"
	MSG_WORK="работает"
	MSG_NOTWORK="не работает"
	MSG_WHITELISTS="ограничен! Белые списки!"
	MSG_DNSTEST="DNS тест"
	MSG_MANUFACTURER="Производитель"
	MSG_MFIRMWATE="Прошивка модема"
	MSG_MODEL="Модель"
	MSG_MODEMSTATE="Состояние модема"
	MSG_PIN="PIN-код симкарты"
	MSG_OPERATOR="Оператор"
	MSG_REGISTRATION="Регистрация"
	MSG_NETWORK="Сеть"
	MSG_SIGNAL="Сигнал"
	MSG_VALUE="значение"
	MSG_ONINT="на интерфейс"
	MSG_INTNOTSET="не задан интерфейс"
	MSG_GOTDHCP="в dnsmasq используется"
	MSG_NOTCONF="не настроен!"
	MSG_MS="мс"
	MSG_NONE="нет"
	MSG_MODEMETH="Сетевые интерфейсы модема не найдены!"
	MSG_SYSTEM="СИСТЕМА"
	MSG_MODEM="МОДЕМ"
	MSG_INTERFACEMODEM="ИНТЕРФЕЙС MODEM"
	MSG_NETWORKMODEM="СЕТЬ"
	MSG_INTERNETMODEM="ИНТЕРНЕТ"
	MSG_INTERNETMODEM_LC="Интернет"
	MSG_LOSS="Процент потерь пакетов"
	MSG_USBDEBUG="Отладка USB"
	MSG_NETMODE="Режим сети"
	MSG_FIREWALL="Межсетевой экран"
	MSG_FWZONE_ERR="Интерфейсу modem не задана зона WAN!"
	MSG_FWZONE_OK="зона WAN"
	MSG_FWZONE_WRONG="Неправильно задана зона, зона -"
	MSG_FWZONE_EXPECTED="(ожидается зона WAN)"
else
	MSG_USB="USB devices not found!"
	MSG_USB_HELP="Wait 30 seconds and run the diagnostics again. Check the device connection to the router. Inspect the cable and all connections. Try connecting a known working device to the router's USB port, wait 30 seconds, and run the diagnostics again. If the router's USB port is functioning correctly, the diagnostics will detect the connected device."
	MSG_MODEM_ON="Automodem: enabled"
	MSG_MODEM_OFF="Automodem: disabled"
	MSG_ENABLED="enabled"
	MSG_DISABLED="disabled"
	MSG_USBDEVFOUND="USB devices found:"
	MSG_MODEMFOUND="Modem found:"
	MSG_MODEMCANDIDATE="MODEM?:"
	MSG_FOUNDDEVICES="Found devices attached to /dev:"
	MSG_AVAILABLE="available"
	MSG_ALL="all"
	MSG_BANDS="Bands"
	MSG_LIMITED="limited"
	MSG_INTMODEM="Interface modem:"
	MSG_SET="set"
	MSG_NOTSETAPN="Not set!"
	MSG_NOTSET="Not set!"
	MSG_PROTO="Protocol"
	MSG_USE="Use"
	MSG_YES="yes"
	MSG_NO="no"
	MSG_TIMEOUT="Timeout"
	MSG_USBETHFOUND="USB network interfaces found"
	MSG_ROUTING="Routing"
	MSG_ROUTINGDEF="default route set"
	MSG_ROUTINGBACKUP="in backup (another connection has priority)"
	MSG_ROUTINGNOTDEF="exists, but not default"
	MSG_ROUTINGFAIL="fail"
	MSG_WORK="ok"
	MSG_NOTWORK="fail"
	MSG_WHITELISTS="white lists!"
	MSG_DNSTEST="DNS test"
	MSG_MANUFACTURER="Manufacturer"
	MSG_MFIRMWATE="Modem Firmware"
	MSG_MODEL="Model"
	MSG_MODEMSTATE="Modem state"
	MSG_PIN="SIM PIN"
	MSG_OPERATOR="Operator"
	MSG_REGISTRATION="Registration"
	MSG_NETWORK="Network"
	MSG_SIGNAL="Signal"
	MSG_VALUE="value"
	MSG_ONINT="on interface"
	MSG_INTNOTSET="interface not set"
	MSG_GOTDHCP="used in dnsmasq"
	MSG_NOTCONF="is not configured!"
	MSG_MS="ms"
	MSG_NONE="none"
	MSG_MODEMETH="No modem network interfaces found!"
	MSG_SYSTEM="SYSTEM"
	MSG_MODEM="MODEM"
	MSG_INTERFACEMODEM="INTERFACE MODEM"
	MSG_NETWORKMODEM="NETWORK"
	MSG_INTERNETMODEM="INTERNET"
	MSG_INTERNETMODEM_LC="Internet"
	MSG_LOSS="Packet loss"
	MSG_USBDEBUG="USB Debug"
	MSG_NETMODE="Network Mode"
	MSG_FIREWALL="Firewall"
	MSG_FWZONE_ERR="Interface modem is not assigned to WAN zone!"
	MSG_FWZONE_OK="WAN zone"
	MSG_FWZONE_WRONG="Incorrect zone assigned -"
	MSG_FWZONE_EXPECTED="(expected WAN zone)"
fi

USBDEVICES="$(lsusb | grep -viE 'host controller|root hub')"
if [ -z "$USBDEVICES" ]; then
	echo -e "\033[1;31m$MSG_USB\033[0m"
	echo
	echo "$MSG_USB_HELP"
	exit 0
fi

echo -e "\033[1m$MSG_SYSTEM:\033[0m"
if [ "$(uci -q get modeminfo.@general[0].automodem)" = "1" ]; then
	echo "$MSG_MODEM_ON"
else
	echo -e "\033[31m$MSG_MODEM_OFF\033[0m"
fi

if [ "$(uci -q get modeminfo.@general[0].ttlfix)" = "1" ]; then
	TI=$(uci -q get modeminfo.@general[0].iface)
	TTL=$(uci -q get modeminfo.@general[0].ttl)
	TTL_OUT="$TTL"
	if [ -n "$TTL" ] && [ "$TTL" -eq "$TTL" ] 2>/dev/null; then
		if [ "$TTL" -lt 63 ] || [ "$TTL" -gt 128 ]; then
			TTL_OUT="\033[31m$TTL\033[0m"
		fi
	fi
	if [ -z "$TI" ]; then
		echo -e "Router TTL Fix: $MSG_ENABLED, \033[31m$MSG_INTNOTSET\033[0m, $MSG_VALUE $TTL_OUT"
	else
		echo -e "Router TTL Fix: $MSG_ENABLED, $MSG_ONINT: $TI, $MSG_VALUE $TTL_OUT"
	fi
else
	echo -e "Router TTL Fix: \033[31m$MSG_DISABLED\033[0m"
fi

echo
echo -e "\033[1mUSB:\033[0m"
echo "$MSG_USBDEVFOUND $USBDEVICES"
USBVP="$(echo "$USBDEVICES" | sed -n 's/.*ID \([0-9a-f]\+:[0-9a-f]\+\).*/\1/p')"

echo
MODEM=""
if [ -f /usr/share/modeminfo/modem.list ]; then
	MODEM=$(grep -i "^${USBVP//:};" /usr/share/modeminfo/modem.list 2>/dev/null)
fi

VID_RAW="${USBVP%%:*}"
PID_RAW="${USBVP##*:}"
VID_TRIM=$(echo "$VID_RAW" | sed 's/^0*//')
PID_TRIM=$(echo "$PID_RAW" | sed 's/^0*//')
SEARCH_KEY="$VID_TRIM/$PID_TRIM/*"
SEARCH_KEY_FULL="$VID_RAW/$PID_RAW/*"

MODEMMODEL_UC=""
if [ -f /usr/share/automodem/modems.conf ]; then
	while IFS=';' read -r pattern model rest; do
		pattern=$(echo "$pattern" | xargs)
		case "$pattern" in \#*) continue ;; esac
		case "$SEARCH_KEY" in
			$pattern)
				MODEMMODEL_UC=$(echo "$model" | xargs | tr 'a-z' 'A-Z')
				break
				;;
		esac
		case "$SEARCH_KEY_FULL" in
			$pattern)
				MODEMMODEL_UC=$(echo "$model" | xargs | tr 'a-z' 'A-Z')
				break
				;;
		esac
	done < /usr/share/automodem/modems.conf
elif [ -f /usr/share/automodem/checkmodel ]; then
	USBVP_FMT="$VID_TRIM/$PID_TRIM/*"
	. /usr/share/automodem/checkmodel "$USBVP_FMT"
	MODEMMODEL_UC=$(echo "$MODEMMODEL" | tr 'a-z' 'A-Z')
fi

IS_CANDIDATE=0
if [ -n "$MODEM" ] || [ -n "$MODEMMODEL_UC" ]; then
	echo -e "\033[1m$MSG_MODEM:\033[0m"
	
	if [ -n "$MODEMMODEL_UC" ] && [ "$MODEMMODEL_UC" != "DEFAULT" ]; then
		echo "$MSG_MODEMFOUND $MODEMMODEL_UC"
	else
		echo "$MSG_MODEMFOUND $MODEM"
	fi
else
	echo -e "\033[1m$MSG_MODEMCANDIDATE\033[0m"
	echo
	IS_CANDIDATE=1
fi

DEV=$(ls /dev/cdc* /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS* 2>/dev/null | sort | awk '{printf sep $0; sep=", "}')

if [ -z "$DEV" ]; then
	echo -e "$MSG_FOUNDDEVICES \033[31m$MSG_NONE\033[0m"
else
	echo "$MSG_FOUNDDEVICES $DEV"
fi

if [ "$IS_CANDIDATE" = "1" ]; then
	echo
	echo "$MSG_USBDEBUG:"
	for vp in $USBVP; do
		vid="${vp%%:*}"
		pid="${vp##*:}"
		if [ -f /sys/kernel/debug/usb/devices ]; then
			awk -v v="$vid" -v p="$pid" -v RS='' 'tolower($0) ~ "vendor=" tolower(v) ".*prodid=" tolower(p) { print }' /sys/kernel/debug/usb/devices
		fi
	done
fi

if mmcli -m any -J >/tmp/mm.json 2>/dev/null; then
	echo
	echo "ModemManager: $MSG_AVAILABLE"

	MANUFACTURER="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic.manufacturer')"
	MODEL="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic.model')"
	REVISION="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic.revision' | tr '\n' ' ' | tr -s ' ')"
	DRIVERS="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic.drivers' | tr -d '[]" ')"
	IMEI="$(jsonfilter -i /tmp/mm.json -e '@.modem["3gpp"]["imei"]')"

	STATE="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic.state')"
	OPERATOR="$(jsonfilter -i /tmp/mm.json -e '@.modem["3gpp"]["operator-name"]')"
	TECH="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic["access-technologies"][0]' | tr 'a-z' 'A-Z')"
	SIGNAL="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic["signal-quality"].value')"
	REG="$(jsonfilter -i /tmp/mm.json -e '@.modem["3gpp"]["registration-state"]')"
	LOCKS="$(jsonfilter -i /tmp/mm.json -e '@.modem["3gpp"]["enabled-locks"]')"

	IFACE="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic.ports' | tr ',' '\n' | sed -n 's/.*\b\([a-z0-9]\+\)\s*(net).*/\1/p')"

	NETMODE="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic["current-modes"]')"
	SUPPORTED="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic["supported-bands"][*]' | tr -d '[]" ' | tr ',' '\n')"
	CURRENT="$(jsonfilter -i /tmp/mm.json -e '@.modem.generic["current-bands"][*]' | tr -d '[]" ' | tr ',' '\n')"

	echo "$MSG_MANUFACTURER: $MANUFACTURER"
	echo "$MSG_MODEL: $MODEL"
	echo "IMEI: $IMEI"
	echo "$MSG_MFIRMWATE: $REVISION"
	echo "$MSG_PROTO: $DRIVERS"
	echo "$MSG_MODEMSTATE: $STATE"
	if echo "$LOCKS" | grep -qw sim-pin; then
		echo "$MSG_PIN: $MSG_ENABLED"
	else
		echo "$MSG_PIN: $MSG_DISABLED"
	fi
	echo "$MSG_REGISTRATION: $REG"
	echo "$MSG_OPERATOR: $OPERATOR"
	echo "$MSG_NETWORK: $TECH"
	echo "$MSG_SIGNAL: $SIGNAL%"
	echo "$MSG_NETMODE: $NETMODE"

	DISABLED=""
	DISABLED="$(echo "$SUPPORTED" | while read b; do
		[ -z "$b" ] && continue
		echo "$CURRENT" | grep -qx "$b" || printf "%s, " "$b"
	done | sed 's/, $//')"

	if [ -z "$DISABLED" ]; then
		echo "$MSG_BANDS: $MSG_ALL"
	else
		echo "$MSG_BANDS, $MSG_LIMITED: $DISABLED"
	fi
fi

check_iface_internet() {
	local iface="$1"
	local ok=0
	local ip
	ip link show "$iface" >/dev/null 2>&1 || {
		echo -e "\033[31m$MSG_INTMODEM $iface $MSG_NOTCONF\033[0m"
		return 1
	}

	if ip link show "$iface" | grep -q "LOWER_UP"; then
		echo "Link $iface: up"
	else
		echo -e "Link $iface: \033[31mdown\033[0m"
	fi

	if ip -4 addr show "$iface" | grep -q "inet "; then
		ip="$(ip -4 addr show "$iface" | awk '/inet / {print $2}' | head -n1)"
		echo "IP: $ip"
	else
		echo -e "IP: \033[31m$MSG_NONE\033[0m"
		return 0
	fi

	if ip route | grep -qE "^default.*dev $iface( |$)"; then
		local active_dev=$(ip route | awk '/^default/ {m=0; d=""; for(i=1;i<=NF;i++){if($i=="metric") m=$(i+1); if($i=="dev") d=$(i+1);} if(min=="" || m<min){min=m; best=d;}} END {print best}')
		if [ -n "$active_dev" ] && [ "$active_dev" != "$iface" ]; then
			echo -e "\033[33m$MSG_ROUTING: $MSG_ROUTINGBACKUP\033[0m"
		else
			echo "$MSG_ROUTING: $MSG_ROUTINGDEF"
		fi
	else
		if ip route | grep -qE "dev $iface( |$)"; then
			echo -e "\033[31m$MSG_ROUTING: $MSG_ROUTINGNOTDEF\033[0m"
		else
			echo -e "\033[31m$MSG_ROUTING: $MSG_ROUTINGFAIL\033[0m"
		fi
	fi

	ping_iface() {
		ping -I "$iface" -c 2 -W 3 "$1" >/dev/null 2>&1
	}

	ping_iface 1.1.1.1 && ok=$((ok + 1))
	ping_iface 8.8.8.8 && ok=$((ok + 1))
	ping_iface 77.88.8.8 && ok=$((ok + 1))

	if [ "$ok" -ge 1 ]; then
		echo
		echo -e "\033[1mDNS FAILSAFE PROXY:\033[0m"

		DFP_IP=$(uci -q get dns-failsafe-proxy.main.listen_ip)
		DFP_PORT=$(uci -q get dns-failsafe-proxy.main.listen_port)

		EXPECTED_SERVER="$DFP_IP#$DFP_PORT"
		DNSMASQ_SERVER=$(uci -q get dhcp.@dnsmasq[0].server)

		if [ "$DNSMASQ_SERVER" = "$EXPECTED_SERVER" ]; then
			echo "$MSG_USE: $MSG_YES ($DNSMASQ_SERVER)"
			USED=1
		else
			echo -e "$MSG_USE: \033[31m$MSG_NO\033[0m (DFP: $EXPECTED_SERVER, $MSG_GOTDHCP ${DNSMASQ_SERVER:-empty})"
			USED=0
		fi

		DNS_IP=$(uci -q get dns-failsafe-proxy.main.dns_ip)
		DNS_PORT=$(uci -q get dns-failsafe-proxy.main.dns_port)
		FB_IP=$(uci -q get dns-failsafe-proxy.main.failback_ip)
		FB_PORT=$(uci -q get dns-failsafe-proxy.main.failback_port)
		EXPECTED_DNS="$DNS_IP#$DNS_PORT"
		EXPECTED_FB="$FB_IP#$FB_PORT"

		if [ "$USED" -eq 1 ]; then
			DFP=$(uci -q get dns-failsafe-proxy.main.session_timeout)
			if [ "$DFP" -gt 400 ] 2>/dev/null; then
				DFP_OUT="\033[1;31m$DFP\033[0m"
			else
				DFP_OUT="$DFP"
			fi
			echo -e "$MSG_TIMEOUT: $DFP_OUT $MSG_MS"
			DNS1_IP=$(uci -q get dns-failsafe-proxy.main.dns_ip)
			DNS1_PORT=$(uci -q get dns-failsafe-proxy.main.dns_port)
			DNS2_IP=$(uci -q get dns-failsafe-proxy.main.failback_ip)
			DNS2_PORT=$(uci -q get dns-failsafe-proxy.main.failback_port)
			DNS1="$DNS1_IP:$DNS1_PORT"
			DNS2="$DNS2_IP:$DNS2_PORT"
			echo "DNS1: $DNS1"
			echo "DNS2: $DNS2"
		fi
	fi

	if [ "$ok" -ge 1 ]; then
	echo
	echo -e "\033[1mDNS:\033[0m"
		if nslookup routerich.ru >/dev/null 2>&1; then
			echo "$MSG_DNSTEST: DNS $MSG_WORK"
		else
			echo -e "\033[31m$MSG_DNSTEST: DNS $MSG_NOTWORK\033[0m"
		fi
	fi

	local loss="100%"
	loss=$(ping -I "$iface" -c 5 -W 2 77.88.8.8 2>/dev/null | awk -F'[,:]' '/packet loss/ {for(i=1;i<=NF;i++) if($i ~ /%/) {sub(/^[ \t]+/, "", $i); split($i, a, " "); print a[1]}}')
	[ -z "$loss" ] && loss="100%"

	echo
	echo -e "\033[1m$MSG_INTERNETMODEM:\033[0m"
	if [ "$ok" -ge 2 ]; then
		echo "$MSG_INTERNETMODEM_LC: $MSG_WORK"
		echo "$MSG_LOSS: $loss"
		return 0
	elif [ "$ok" -eq 1 ]; then
		echo -e "\033[31m$MSG_INTERNETMODEM_LC: $MSG_WHITELISTS\033[0m"
		echo "$MSG_LOSS: $loss"
		return 1
	else
		echo -e "\033[31m$MSG_INTERNETMODEM_LC: $MSG_NOTWORK\033[0m"
		echo "$MSG_LOSS: $loss"
		return 1
	fi
}

echo
echo -e "\033[1m$MSG_INTERFACEMODEM:\033[0m"
INT=$(uci -q get network.modem)
PROTO=$(uci -q get network.modem.proto)
APN=$(uci -q get network.modem.apn)
if [ -n "$INT" ]; then
	echo "$MSG_INTMODEM $MSG_SET"
	echo "$MSG_PROTO: $PROTO"
	if [ "$PROTO" != "dhcp" ]; then
		if [ -n "$APN" ]; then
			echo "APN: $APN"
		else
			echo -e "\033[31mAPN: $MSG_NOTSETAPN\033[0m"
		fi
	fi
else
	echo -e "\033[31m$MSG_INTMODEM $MSG_NOTSET\033[0m"
fi

modem_zone=""
z=$(uci -q show firewall | awk -F. '/\.network=.*[^a-zA-Z0-9_]modem[^a-zA-Z0-9_]/{print $2; exit}')
[ -n "$z" ] && modem_zone=$(uci -q get firewall."$z".name)

if [ "$modem_zone" = "wan" ]; then
	echo "$MSG_FIREWALL: $MSG_FWZONE_OK"
elif [ -n "$modem_zone" ]; then
	modem_zone_uc=$(echo "$modem_zone" | tr 'a-z' 'A-Z')
	echo -e "$MSG_FIREWALL: \033[31m$MSG_FWZONE_WRONG $modem_zone_uc $MSG_FWZONE_EXPECTED\033[0m"
else
	echo -e "$MSG_FIREWALL: \033[31m$MSG_FWZONE_ERR\033[0m"
fi

FOUND=0
for iface in /sys/class/net/*; do
	iface=$(basename "$iface")
	devpath=$(readlink -f /sys/class/net/$iface/device)
	if echo "$devpath" | grep -q "/usb"; then
		FOUND=1
		echo
		echo -e "\033[1m$MSG_NETWORKMODEM:\033[0m"
		echo "$MSG_USBETHFOUND: $iface"
		check_iface_internet "$iface"
	fi
done

if [ "$FOUND" -eq 0 ]; then
	echo
	echo -e "\033[1m$MSG_NETWORKMODEM:\033[0m"
	echo -e "\033[31m$MSG_MODEMETH\033[0m"
fi
