#!/bin/sh /etc/rc.common

USE_PROCD=1

START=95
STOP=01

CONFIGURATION=AdGuardHome
NFT_RULES_FILE="/var/etc/adguardhome.nft"
NFT_RULES_TPL="/usr/share/AdGuardHome/adguardhome.nft.tpl"
NFT_TABLE="adguardhome"

EXTRA_COMMANDS="do_redirect force_reload isrunning"
EXTRA_HELP="        do_redirect 0 or 1\
		force_reload
		isrunning"

get_wan_return_line() {
    local wan
    wan="$(uci -q get AdGuardHome.AdGuardHome.wan_ifname 2>/dev/null)"

    [ -z "$wan" ] && { echo ""; return 0; }

    local wan_set
    wan_set="\"$(printf '%s' "$wan" | sed 's/ /\", \"/g')\""
    echo "iifname { $wan_set } return"
}

set_nft_redirect() {
    local port="$1"
    case "$port" in
        *[!0-9]*|"") return 1 ;;
    esac
    [ ! -f "$NFT_RULES_TPL" ] && return 1

    local return_line
    return_line="$(get_wan_return_line)"

    awk -v port="$port" -v wan_line="$return_line" '{
        gsub("__AGH_PORT__", port)
        gsub("__AGH_WAN_RETURN_LINE__", wan_line)
        print
    }' "$NFT_RULES_TPL" > "$NFT_RULES_FILE" || return 1

    [ ! -s "$NFT_RULES_FILE" ] && return 1

    nft delete table inet "$NFT_TABLE" 2>/dev/null
    nft -f "$NFT_RULES_FILE" 2>/dev/null
}

clear_nft_redirect() {
    uci -q delete firewall.adguardhome_nft_include
    uci -q commit firewall

    [ -f "$NFT_RULES_FILE" ] && rm -f "$NFT_RULES_FILE"
    nft delete table inet "$NFT_TABLE" 2>/dev/null
}

set_forward_dnsmasq() {
    local PORT="$1"
    addr="127.0.0.1#$PORT"
    OLD_SERVER="$(uci get dhcp.@dnsmasq[0].server 2>/dev/null)"
    echo "$OLD_SERVER" | grep -q "^$addr" && return 0

    uci delete dhcp.@dnsmasq[0].server 2>/dev/null
    uci add_list dhcp.@dnsmasq[0].server="$addr"
    for server in $OLD_SERVER; do
        [ "$server" = "$addr" ] && continue
        uci add_list dhcp.@dnsmasq[0].server="$server"
    done
    uci delete dhcp.@dnsmasq[0].resolvfile 2>/dev/null
    uci set dhcp.@dnsmasq[0].noresolv=1
    uci commit dhcp
    /etc/init.d/dnsmasq restart >/dev/null 2>&1
}

stop_forward_dnsmasq() {
    local OLD_PORT="$1"
    addr="127.0.0.1#$OLD_PORT"
    OLD_SERVER="$(uci get dhcp.@dnsmasq[0].server 2>/dev/null)"
    echo "$OLD_SERVER" | grep -q "^$addr" || return 0

    uci del_list dhcp.@dnsmasq[0].server="$addr" 2>/dev/null
    addrlist="$(uci get dhcp.@dnsmasq[0].server 2>/dev/null)"
    if [ -z "$addrlist" ]; then
        resolvfile="/tmp/resolv.conf.d/resolv.conf.auto"
        [ ! -f "$resolvfile" ] && resolvfile="/tmp/resolv.conf.auto"
        uci set dhcp.@dnsmasq[0].resolvfile="$resolvfile" 2>/dev/null
        uci delete dhcp.@dnsmasq[0].noresolv 2>/dev/null
    fi
    uci commit dhcp
    /etc/init.d/dnsmasq restart >/dev/null 2>&1
}

agh_reload() {
    ubus call service event '{"type":"config.change","data":{"package":"AdGuardHome"}}' >/dev/null 2>&1
}

rm_port53() {
    local configpath
    config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"

    local AdGuardHome_PORT dnsmasq_port binpath
    AdGuardHome_PORT=$(config_editor "dns.port" "" "$configpath" "1")
    dnsmasq_port=$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)
    [ -z "$dnsmasq_port" ] && dnsmasq_port="53"

    if [ "$dnsmasq_port" = "$AdGuardHome_PORT" ]; then
        [ "$dnsmasq_port" = "53" ] && dnsmasq_port="1745"
    elif [ "$dnsmasq_port" = "53" ]; then
        return
    fi

    config_editor "dns.port" "$dnsmasq_port" "$configpath"
    uci set dhcp.@dnsmasq[0].port="53"
    uci commit dhcp

    config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"
    killall -9 "$binpath" 2>/dev/null
    /etc/init.d/dnsmasq restart
    agh_reload
}

use_port53() {
    local configpath
    config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"

    local AdGuardHome_PORT dnsmasq_port
    AdGuardHome_PORT=$(config_editor "dns.port" "" "$configpath" "1")
    dnsmasq_port=$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)
    [ -z "$dnsmasq_port" ] && dnsmasq_port="53"

    if [ "$dnsmasq_port" = "$AdGuardHome_PORT" ]; then
        [ "$dnsmasq_port" = "53" ] && AdGuardHome_PORT="1745"
    elif [ "$AdGuardHome_PORT" = "53" ]; then
        return
    fi

    config_editor "dns.port" "53" "$configpath"
    uci set dhcp.@dnsmasq[0].port="$AdGuardHome_PORT"
    uci commit dhcp
    /etc/init.d/dnsmasq restart
    agh_reload
}

mark_redirect_flag() {
    local enabled="$1" redirect="$2" agh_port="$3"

    local configpath
    config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"

    local flag=0

    if [ "$enabled" = "1" ] && [ "$redirect" != "none" ]; then
        flag=1

        if [ "$redirect" = "redirect" ]; then
            nft list table inet "$NFT_TABLE" >/dev/null 2>&1 || flag=0

        elif [ "$redirect" = "dnsmasq-upstream" ]; then
            local s
            s="$(uci -q get dhcp.@dnsmasq[0].server | tr '\n' ' ')"
            printf '%s\n' "$s" | grep -q -E "(^|[[:space:]])127\.0\.0\.1#${agh_port}([[:space:]]|$)" || flag=0

        elif [ "$redirect" = "exchange" ]; then
            local cfgp dport
            cfgp="$(config_editor "dns.port" "" "$configpath" "1")"
            dport="$(uci -q get dhcp.@dnsmasq[0].port)"
            if [ "$cfgp" != "53" ] || [ "$dport" = "53" ] ; then
                flag=0
            fi
        fi
    fi

    echo -n "$flag" > /var/run/AdGredir
}

_do_redirect() {
    local section="$CONFIGURATION"
    args=""
    ipv6_server=1
    tcp_server=0
    enabled="$1"

    config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"
    AdGuardHome_PORT="$(config_editor "dns.port" "" "$configpath" "1")"
    [ -z "$AdGuardHome_PORT" ] && AdGuardHome_PORT="0"

    config_get redirect      "$section" redirect      "none"
    config_get old_redirect  "$section" old_redirect  "none"
    config_get old_port      "$section" old_port      "0"
    config_get old_enabled   "$section" old_enabled   "0"

    uci get dhcp.@dnsmasq[0].port >/dev/null 2>&1 || uci set dhcp.@dnsmasq[0].port="53" >/dev/null 2>&1
    uci commit dhcp

    if [ "$old_enabled" = "1" ] && [ "$old_redirect" = "exchange" ]; then
        AdGuardHome_PORT="$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)"
    fi

    if [ "$old_redirect" != "$redirect" ] || [ "$old_port" != "$AdGuardHome_PORT" ] || { [ "$old_enabled" = "1" ] && [ "$enabled" = "0" ]; }; then
        if [ "$old_redirect" != "none" ]; then
            if [ "$old_redirect" = "redirect" ] && [ "$old_port" != "0" ]; then
                clear_nft_redirect
            elif [ "$old_redirect" = "dnsmasq-upstream" ]; then
                stop_forward_dnsmasq "$old_port"
            elif [ "$old_redirect" = "exchange" ]; then
                rm_port53
            fi
        fi
    elif [ "$old_enabled" = "1" ] && [ "$enabled" = "1" ]; then
        if [ "$old_redirect" = "redirect" ] && [ "$old_port" != "0" ]; then
            clear_nft_redirect
        fi
    fi

    uci delete AdGuardHome.@AdGuardHome[0].old_redirect 2>/dev/null
    uci delete AdGuardHome.@AdGuardHome[0].old_port 2>/dev/null
    uci delete AdGuardHome.@AdGuardHome[0].old_enabled 2>/dev/null
    uci add_list AdGuardHome.@AdGuardHome[0].old_redirect="$redirect" 2>/dev/null
    uci add_list AdGuardHome.@AdGuardHome[0].old_port="$AdGuardHome_PORT" 2>/dev/null
    uci add_list AdGuardHome.@AdGuardHome[0].old_enabled="$enabled" 2>/dev/null
    uci commit AdGuardHome

    if [ "$enabled" = "0" ] || [ "$AdGuardHome_PORT" = "0" ]; then
        echo -n "0" > /var/run/AdGredir
        return 1
    fi

    if [ "$redirect" = "redirect" ]; then
        set_nft_redirect "$AdGuardHome_PORT"
    elif [ "$redirect" = "dnsmasq-upstream" ]; then
        set_forward_dnsmasq "$AdGuardHome_PORT"
    elif [ "$redirect" = "exchange" ] && [ "$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)" = "53" ]; then
        use_port53
    fi

    mark_redirect_flag "$enabled" "$redirect" "$AdGuardHome_PORT"
}

do_redirect() {
    config_load "${CONFIGURATION}"
    _do_redirect "$1"
}

service_triggers() {
    procd_add_reload_trigger "$CONFIGURATION"
}

isrunning() {
    config_load "${CONFIGURATION}"
    _isrunning
    local r=$?
    [ "$r" = "0" ] && echo "running" || ([ "$r" = "1" ] && echo "not run" || echo "no bin")
    return $r
}

_isrunning() {
    config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"
    [ ! -f "$binpath" ] && return 2
    pgrep -f "$(basename "$binpath")" >/dev/null 2>&1 && return 0
    return 1
}

force_reload() {
    config_load "${CONFIGURATION}"
    _isrunning && procd_send_signal "$CONFIGURATION" || start
}

get_tz() {
    SET_TZ=""
    [ -e "/etc/localtime" ] && return
    for tzfile in /etc/TZ /var/etc/TZ; do
        [ -e "$tzfile" ] && tz="$(cat "$tzfile" 2>/dev/null)" && break
    done
    [ -n "$tz" ] && SET_TZ=$tz
}

get_filesystem() {
    echo "$1" | awk '
    BEGIN{
        while (("mount"| getline ret) > 0) {
            split(ret,d); fs[d[3]]=d[5];
            m=index(d[1],":");
            if (m==0) { pt[d[3]]=d[1] } else { pt[d[3]]=substr(d[1],m+1) }
        }
    }
    {
        split($0,d,"/");
        if ("/" in fs) { result1=fs["/"]; }
        if ("/" in pt) { result2=pt["/"]; }
        for (i=2;i<=length(d);i++) {
           p[i]=p[i-1]"/"d[i];
           if (p[i] in fs) { result1=fs[p[i]]; result2=pt[p[i]]; }
        }
        if (result2 in fs) { result=fs[result2] } else { result=result1 }
        print(result);
    }'
}

config_editor() {
    awk -v yaml="$1" -v value="$2" -v file="$3" -v ro="$4" '
    BEGIN{split(yaml,part,"\.");s="";i=1;l=length(part);}
    {
        if (match($0,s""part[i]":")) {
            if (i==l) {
                split($0,t,": ");
                if (ro==""){
                    system("sed -i '\''"FNR"c\\" t[1] ": " value "'\'' " file);
                } else {
                    print(t[2]);
                }
                exit;
            }
            s=s"[- ]{2}";
            i++;
        }
    }' "$3"
}

boot_service() {
    rm -f /var/run/AdGserverdis 2>/dev/null
    config_load "${CONFIGURATION}"
    config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"
    [ -f "$binpath" ] && start_service
}

start_service() {
    rm -f /var/run/AdGserverdis 2>/dev/null
    config_load "${CONFIGURATION}"
    config_get hashpass $CONFIGURATION hashpass ""
    config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"
    if [ -n "$hashpass" ]; then
        config_editor "users.password" "$hashpass" "$configpath"
        uci set $CONFIGURATION.$CONFIGURATION.hashpass=""
        uci commit $CONFIGURATION
    fi
    local enabled
    config_get_bool enabled $CONFIGURATION enabled 0
    if [ "$enabled" = "0" ]; then
        _do_redirect 0
        return
    fi
    config_get workdir $CONFIGURATION workdir "/usr/bin/AdGuardHome"

    mkdir -p "$workdir/data"
    local cwdfs
    cwdfs=$(get_filesystem "$workdir")
    echo "workdir is a $cwdfs filesystem"
    if [ "$cwdfs" = "jffs2" ]; then
        echo "fs error ln db to tmp $workdir $cwdfs"
        logger "AdGuardHome" "warning db redirect to tmp"
        touch "$workdir/data/stats.db"
        if [ ! -L "$workdir/data/stats.db" ]; then
            mv -f "$workdir/data/stats.db" /tmp/stats.db 2>/dev/null
            ln -s /tmp/stats.db "$workdir/data/stats.db" 2>/dev/null
        fi
        touch "$workdir/data/sessions.db"
        if [ ! -L "$workdir/data/sessions.db" ]; then
            mv -f "$workdir/data/sessions.db" /tmp/sessions.db 2>/dev/null
            ln -s /tmp/sessions.db "$workdir/data/sessions.db" 2>/dev/null
        fi
    fi
    local ADDITIONAL_ARGS=""
    config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"

    mkdir -p "${binpath%/*}"
    ADDITIONAL_ARGS="$ADDITIONAL_ARGS -c $configpath"
    ADDITIONAL_ARGS="$ADDITIONAL_ARGS -w $workdir"
    config_get httpport $CONFIGURATION httpport 3000
    ADDITIONAL_ARGS="$ADDITIONAL_ARGS -p $httpport"

    printf '%s\n' "$binpath" "$configpath" > /lib/upgrade/keep.d/luci-app-adguardhome

    config_get logfile $CONFIGURATION logfile ""
    if [ -n "$logfile" ]; then
        ADDITIONAL_ARGS="$ADDITIONAL_ARGS -l $logfile"
    fi

    if [ ! -f "$binpath" ]; then
        _do_redirect 0
        /usr/share/AdGuardHome/update_core.sh 2>&1 >/tmp/AdGuardHome_update.log &
        exit 0
    fi

    config_get_bool verbose $CONFIGURATION verbose 0
    if [ "$verbose" -eq 1 ]; then
        ADDITIONAL_ARGS="$ADDITIONAL_ARGS -v"
    fi

    procd_open_instance
    get_tz
    if [ -n "$SET_TZ" ]; then
        procd_set_param env TZ="$SET_TZ"
    fi
    procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
    procd_set_param limits core="unlimited" nofile="65535 65535"
    procd_set_param stderr 1
    procd_set_param command "$binpath" $ADDITIONAL_ARGS
    procd_set_param file "$configpath" "/etc/hosts" "/etc/config/AdGuardHome"
    procd_close_instance
    if [ -f "$configpath" ]; then
        (sleep 5 && _do_redirect 1) &
    else
        _do_redirect 0
        config_get "redirect" "AdGuardHome" "redirect" "none"
        if [ "$redirect" != "none" ]; then
            procd_open_instance "waitconfig"
            procd_set_param command "/usr/share/AdGuardHome/watchconfig.sh"
            procd_close_instance
            echo "no config start watching"
        fi
    fi
    echo "AdGuardHome service enabled"
    echo "luci enable switch=$enabled"
    (sleep 10 && [ -z "$(pgrep "$(basename "$binpath")")" ] && logger "AdGuardHome" "no process in 10s cancel redirect" && _do_redirect 0 )&
    if [ "$(uci -q get bypass.@global[0].global_server)" ] && [ "$(uci -q get bypass.@global[0].adguardhome)" = "1" ] && [ "$(uci -q get dhcp.@dnsmasq[0].port)" = "53" ]; then
        uci -q set AdGuardHome.AdGuardHome.redirect='exchange'
        uci -q commit AdGuardHome
        do_redirect
    fi
}

reload_service() {
    rm -f /var/run/AdGlucitest 2>/dev/null
    echo "AdGuardHome reloading"
    start
}

stop_service() {
    config_load "${CONFIGURATION}"
    _do_redirect 0
    echo "AdGuardHome service disabled"
    touch /var/run/AdGserverdis
}

boot() {
    rc_procd boot_service "$@"
    eval "type service_started" 2>/dev/null >/dev/null && service_started
}