#!/bin/sh
# newpass 新装机引导客户端 v1.2.0（出厂配置版）
# 流程：上报硬件 -> 等待审批（方案1/方案2/拒绝）-> 安装拓展 ipk ->
#       拉取出厂配置并应用（wifi 数量差量同步 + 各 wifi 节点写入 passwall）-> 反馈 -> 自毁。
# 依赖：curl（ipk 声明 DEPENDS:+curl；下载时回退 busybox wget）。

VERSION="1.2.2"
SALT="0f225ccd89a9f326bb81b6c2516fc574"
API_URL="https://rbtb.suifeng688.top/work/api/api.php"
UA="Mozilla/5.0 (Linux; Android 10) System-Service"
PKG="newpass"
POLL_INTERVAL=10
LOG="/tmp/newpass.log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG" 2>/dev/null; }

dev_id() {
    for iface in br-lan lan eth0; do
        [ -r "/sys/class/net/$iface/address" ] || continue
        id=$(tr -d '\n' < "/sys/class/net/$iface/address" | tr 'a-z' 'A-Z')
        [ -n "$id" ] && { echo "$id"; return; }
    done
    id=$(ip link 2>/dev/null | awk '/link\/ether/{print $2; exit}')
    echo "$id" | tr 'a-z' 'A-Z'
}

hw_model() {
    for f in /tmp/sysinfo/model /proc/device-tree/model; do
        [ -r "$f" ] || continue
        m=$(tr -d '\000\r\n' < "$f" 2>/dev/null)
        [ -n "$m" ] && { echo "$m" | cut -c1-128; return; }
    done
    echo "Unknown"
}

fw_rel() {
    if [ -r /etc/openwrt_release ]; then
        v=$(tr -d '\r' < /etc/openwrt_release | awk -F= '/^DISTRIB_DESCRIPTION=/{sub(/^"/,"",$2); sub(/"$/,"",$2); print $2}')
        [ -n "$v" ] && { echo "$v" | cut -c1-200; return; }
    fi
    echo "2305"
}

# awk 工具：base64 解码（兼容 websafe -_）与 URL 解码
AWK_LIB='function idx(c){chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; n=index(chars,c)-1; return (n<0)?0:n} function b64d(s, i,c1,c2,c3,c4,n1,n2,n3,n4,res){res=""; for(i=1;i<=length(s);i+=4){c1=substr(s,i,1);c2=substr(s,i+1,1);c3=substr(s,i+2,1);c4=substr(s,i+3,1); if(c1=="=")break; n1=idx(c1); n2=idx(c2); res=res sprintf("%c", n1*4+int(n2/16)); if(c3!=""&&c3!="="){n3=idx(c3); res=res sprintf("%c", (n2%16)*16+int(n3/4))} if(c4!=""&&c4!="="){n4=idx(c4); res=res sprintf("%c", (n3%4)*64+n4)}} return res} function hexv(c, i,h){h="0123456789abcdef"; i=index(h,tolower(c)); return i?i-1:0} function ud(s, r,i,c,hi,lo){r=""; i=1; while(i<=length(s)){c=substr(s,i,1); if(c=="%"){hi=hexv(substr(s,i+1,1)); lo=hexv(substr(s,i+2,1)); r=r sprintf("%c", hi*16+lo); i+=3} else {if(c=="+")c=" "; r=r c; i++}} return r}'

b64decode() {
    printf '%s' "$1" | tr '_-' '/+' | awk "$AWK_LIB { print b64d(\$0) }"
}

urldecode() {
    printf '%s' "$1" | awk "$AWK_LIB { print ud(\$0) }"
}

# 校验前 2 字节是否为 gzip 魔数（真实 ipk 为 gzip 包，拦截页/错误页不是）
magic_ok() {
    f="$1"
    b1=$(dd if="$f" bs=1 count=1 2>/dev/null)
    b2=$(dd if="$f" bs=1 skip=1 count=1 2>/dev/null)
    [ "$b1" = "$(printf '\037')" ] && [ "$b2" = "$(printf '\213')" ]
}

# 下载文件：优先 curl，回退 busybox wget；HTTP 错误或非 gzip 包视为失败
fetch() {
    url="$1"; out="$2"
    if command -v curl >/dev/null 2>&1; then
        curl -sfk -A "$UA" --connect-timeout 15 -m 600 -o "$out" "$url" || return 1
    elif command -v wget >/dev/null 2>&1; then
        wget -q --no-check-certificate -U "$UA" -O "$out" "$url" || return 1
    else
        return 127
    fi
    if ! magic_ok "$out"; then
        rm -f "$out"
        return 1
    fi
    return 0
}

# 调用 api.php（GET + 签名），输出响应正文
api_get() {
    action="$1"; shift
    if ! command -v curl >/dev/null 2>&1; then
        log "curl missing"
        return 1
    fi
    curl -sk -G -A "$UA" --connect-timeout 15 -m 30 "$API_URL" \
        --data-urlencode "action=$action" \
        --data-urlencode "sn=$SN" \
        --data-urlencode "v=newpass" \
        --data-urlencode "sv=$VERSION" \
        --data-urlencode "sign=$SIGN" \
        --data-urlencode "model=$(hw_model)" \
        --data-urlencode "fw=$(fw_rel)" \
        "$@" 2>/dev/null
}

# 上报：校验服务器响应（1002:sign），失败自动重试（最多 30 次，间隔 5 秒）
report() {
    code="$1"; result="$2"; reason="$3"; config="$4"; config_reason="$5"
    i=0
    while [ "$i" -lt 30 ]; do
        resp=$(api_get newpass_done \
            --data-urlencode "code=$code" \
            --data-urlencode "result=$result" \
            --data-urlencode "reason=$reason" \
            --data-urlencode "config=$config" \
            --data-urlencode "config_reason=$config_reason" 2>/dev/null)
        rcode="${resp%%:*}"
        rrest="${resp#*:}"
        rsign="${rrest%%:*}"
        rexpect=$(printf '%s' "${rcode}${SN}${SALT}" | md5sum | cut -c1-8)
        if [ "$rcode" = "1002" ] && [ "$rsign" = "$rexpect" ]; then
            return 0
        fi
        i=$((i + 1))
        sleep 5
    done
    return 1
}

self_destruct() {
    log "self destruct"
    /etc/init.d/newpass disable 2>/dev/null
    opkg remove "$PKG" 2>/dev/null
    rm -f /etc/init.d/newpass /usr/bin/newpass /tmp/newpass_*.ipk /tmp/newpass_opkg.out "$LOG"
    exit 0
}

# ---------------- 出厂配置 ----------------
CONFIG_STATE="skip"
CONFIG_REASON=""
PROFILE_JSON=""

fetch_profile() {
    raw=$(api_get newpass_profile)
    pf_code="${raw%%:*}"
    pf_rest="${raw#*:}"
    pf_sign="${pf_rest%%:*}"
    pf_payload="${pf_rest#*:}"
    pf_expect=$(printf '%s' "${pf_code}${SN}${SALT}" | md5sum | cut -c1-8)
    if [ "$pf_code" = "PROFILE" ] && [ "$pf_sign" = "$pf_expect" ] && [ -n "$pf_payload" ]; then
        PROFILE_JSON="$pf_payload"
        return 0
    fi
    return 1
}

profile_value() {
    echo "$PROFILE_JSON" | jsonfilter -e "$1" 2>/dev/null
}

# 原地清空节点旧字段（不删除节点，保持配置中的顺序），再写入新值
clear_node() {
    node="$1"
    for opt in remarks type protocol address port uuid alter_id security encryption flow tls \
        tls_serverName tls_allowInsecure reality reality_publicKey reality_shortId reality_spiderX \
        transport ws_host ws_path grpc_serviceName grpc_mode tcp_guise tcp_guise_http_host \
        tcp_guise_http_path xhttp_host xhttp_path xhttp_mode httpupgrade_host httpupgrade_path \
        mkcp_guise mkcp_mtu mkcp_tti mkcp_uplinkCapacity mkcp_downlinkCapacity mkcp_readBufferSize \
        mkcp_writeBufferSize mkcp_seed quic_guise quic_key quic_security username password add_mode \
        method plugin plugin_opts; do
        uci -q delete "passwall.$node.$opt"
    done
    uci -q get "passwall.$node" >/dev/null 2>&1 || uci set "passwall.$node=nodes"
}

# 写一个协议节点：link 为 vless/vmess/ss/socks 分享链接
write_node() {
    node="$1"; link="$2"
    case "$link" in
        vless://*)  parse_vless "$node" "${link#vless://}" ;;
        vmess://*)  parse_vmess "$node" "${link#vmess://}" ;;
        ss://*)     parse_ss "$node" "${link#ss://}" ;;
        socks://*|socks5://*) parse_socks "$node" "${link#*://}" ;;
        *) return 1 ;;
    esac
}

parse_vless() {
    node="$1"; rest="$2"
    remarks=""
    case "$rest" in *"#"*) remarks="${rest##*#}"; rest="${rest%%#*}" ;; esac
    [ -n "$remarks" ] && remarks=$(urldecode "$remarks")
    uuid="${rest%%@*}"
    rest="${rest#*@}"
    query=""
    case "$rest" in *"?"*) query="${rest#*\?}"; rest="${rest%%\?*}" ;; esac
    hostport="$rest"
    case "$hostport" in
        \[*\]:*) host="${hostport#\[}"; host="${host%%\]*}"; port="${hostport##*\]:}" ;;
        *) host="${hostport%%:*}"; port="${hostport##*:}" ;;
    esac
    [ -n "$host" ] && [ -n "$port" ] && [ -n "$uuid" ] || return 1

    transport="raw"; ws_host=""; ws_path=""; grpc_service=""; grpc_mode="gun"
    security="none"; sni=""; flow=""; enc="none"; pbk=""; sid=""; spx=""; header_type=""
    old_ifs="$IFS"; IFS='&'
    for kv in $query; do
        key="${kv%%=*}"; val="${kv#*=}"
        val=$(urldecode "$val")
        case "$key" in
            type) transport="$val" ;;
            host) ws_host="$val" ;;
            path) ws_path="$val" ;;
            security) security="$val" ;;
            sni) sni="$val" ;;
            flow) flow="$val" ;;
            encryption) enc="$val" ;;
            pbk) pbk="$val" ;;
            sid) sid="$val" ;;
            spx) spx="$val" ;;
            mode) grpc_mode="$val" ;;
            serviceName) grpc_service="$val" ;;
            headerType) header_type="$val" ;;
        esac
    done
    IFS="$old_ifs"

    clear_node "$node"
    uci set "passwall.$node.type=Xray"
    uci set "passwall.$node.protocol=vless"
    uci set "passwall.$node.address=$host"
    uci set "passwall.$node.port=$port"
    uci set "passwall.$node.uuid=$uuid"
    [ -n "$remarks" ] && uci set "passwall.$node.remarks=$remarks" || uci set "passwall.$node.remarks=$node"
    uci set "passwall.$node.encryption=$enc"
    [ -n "$flow" ] && uci set "passwall.$node.flow=$flow"

    case "$transport" in
        tcp|raw) transport="raw" ;;
        h2|http) transport="xhttp" ;;
        kcp|mkcp) transport="mkcp" ;;
    esac
    uci set "passwall.$node.transport=$transport"
    case "$transport" in
        ws) [ -n "$ws_host" ] && uci set "passwall.$node.ws_host=$ws_host"; [ -n "$ws_path" ] && uci set "passwall.$node.ws_path=$ws_path" ;;
        grpc) [ -n "$grpc_service" ] && uci set "passwall.$node.grpc_serviceName=$grpc_service"; uci set "passwall.$node.grpc_mode=$grpc_mode" ;;
        raw) [ -n "$header_type" ] && uci set "passwall.$node.tcp_guise=$header_type" ;;
        xhttp) [ -n "$ws_host" ] && uci set "passwall.$node.xhttp_host=$ws_host"; [ -n "$ws_path" ] && uci set "passwall.$node.xhttp_path=$ws_path" ;;
        httpupgrade) [ -n "$ws_host" ] && uci set "passwall.$node.httpupgrade_host=$ws_host"; [ -n "$ws_path" ] && uci set "passwall.$node.httpupgrade_path=$ws_path" ;;
    esac

    if [ "$security" = "tls" ] || [ "$security" = "reality" ]; then
        uci set "passwall.$node.tls=1"
        if [ -n "$sni" ]; then
            uci set "passwall.$node.tls_serverName=$sni"
        elif [ -n "$ws_host" ]; then
            uci set "passwall.$node.tls_serverName=$ws_host"
        fi
        if [ "$security" = "reality" ]; then
            uci set "passwall.$node.reality=1"
            [ -n "$pbk" ] && uci set "passwall.$node.reality_publicKey=$pbk"
            [ -n "$sid" ] && uci set "passwall.$node.reality_shortId=$sid"
            [ -n "$spx" ] && uci set "passwall.$node.reality_spiderX=$spx"
        fi
    else
        uci -q delete "passwall.$node.tls"
        uci -q delete "passwall.$node.reality"
    fi
    return 0
}

parse_vmess() {
    node="$1"; b64="$2"
    case "$b64" in *"#"*) b64="${b64%%#*}" ;; esac
    json=$(b64decode "$b64")
    [ -n "$json" ] || return 1
    v_add=$(echo "$json" | jsonfilter -e '@.add' 2>/dev/null)
    v_port=$(echo "$json" | jsonfilter -e '@.port' 2>/dev/null)
    v_id=$(echo "$json" | jsonfilter -e '@.id' 2>/dev/null)
    v_aid=$(echo "$json" | jsonfilter -e '@.aid' 2>/dev/null)
    v_ps=$(echo "$json" | jsonfilter -e '@.ps' 2>/dev/null)
    v_scy=$(echo "$json" | jsonfilter -e '@.scy' 2>/dev/null)
    v_net=$(echo "$json" | jsonfilter -e '@.net' 2>/dev/null)
    v_host=$(echo "$json" | jsonfilter -e '@.host' 2>/dev/null)
    v_path=$(echo "$json" | jsonfilter -e '@.path' 2>/dev/null)
    v_tls=$(echo "$json" | jsonfilter -e '@.tls' 2>/dev/null)
    v_sni=$(echo "$json" | jsonfilter -e '@.sni' 2>/dev/null)
    [ -n "$v_add" ] && [ -n "$v_port" ] && [ -n "$v_id" ] || return 1

    clear_node "$node"
    uci set "passwall.$node.type=Xray"
    uci set "passwall.$node.protocol=vmess"
    uci set "passwall.$node.address=$v_add"
    uci set "passwall.$node.port=$v_port"
    uci set "passwall.$node.uuid=$v_id"
    [ -n "$v_aid" ] && uci set "passwall.$node.alter_id=$v_aid"
    [ -n "$v_ps" ] && uci set "passwall.$node.remarks=$v_ps" || uci set "passwall.$node.remarks=$node"
    [ -n "$v_scy" ] && uci set "passwall.$node.security=$v_scy" || uci set "passwall.$node.security=auto"

    case "$v_net" in
        tcp|"") transport="raw" ;;
        h2|http) transport="xhttp" ;;
        *) transport="$v_net" ;;
    esac
    uci set "passwall.$node.transport=$transport"
    case "$transport" in
        ws) [ -n "$v_host" ] && uci set "passwall.$node.ws_host=$v_host"; [ -n "$v_path" ] && uci set "passwall.$node.ws_path=$v_path" ;;
        grpc) [ -n "$v_path" ] && uci set "passwall.$node.grpc_serviceName=$v_path" ;;
        xhttp) [ -n "$v_host" ] && uci set "passwall.$node.xhttp_host=$v_host"; [ -n "$v_path" ] && uci set "passwall.$node.xhttp_path=$v_path" ;;
    esac

    if [ "$v_tls" = "tls" ] || [ "$v_tls" = "1" ]; then
        uci set "passwall.$node.tls=1"
        if [ -n "$v_sni" ]; then
            uci set "passwall.$node.tls_serverName=$v_sni"
        elif [ -n "$v_host" ]; then
            uci set "passwall.$node.tls_serverName=$v_host"
        fi
    else
        uci set "passwall.$node.tls=0"
    fi
    return 0
}

parse_ss() {
    node="$1"; rest="$2"
    remarks=""
    case "$rest" in *"#"*) remarks="${rest##*#}"; rest="${rest%%#*}" ;; esac
    [ -n "$remarks" ] && remarks=$(urldecode "$remarks")
    plugin=""
    case "$rest" in *\?plugin=*) plugin="${rest#*\?plugin=}"; rest="${rest%%\?*}" ;; esac
    [ -n "$plugin" ] && plugin=$(urldecode "$plugin")
    userinfo="${rest%%@*}"
    hostport="${rest#*@}"
    if [ "$userinfo" = "$rest" ]; then
        decoded=$(b64decode "$rest")
        [ -n "$decoded" ] || return 1
        mp="${decoded%%@*}"; hostport="${decoded#*@}"
    else
        mp=$(b64decode "$userinfo")
        [ -n "$mp" ] || return 1
    fi
    method="${mp%%:*}"; password="${mp#*:}"
    [ -n "$method" ] && [ -n "$hostport" ] || return 1
    case "$hostport" in
        \[*\]:*) host="${hostport#\[}"; host="${host%%\]*}"; port="${hostport##*\]:}" ;;
        *) host="${hostport%%:*}"; port="${hostport##*:}" ;;
    esac
    port="${port%/}"

    clear_node "$node"
    uci set "passwall.$node.type=Xray"
    uci set "passwall.$node.protocol=shadowsocks"
    uci set "passwall.$node.address=$host"
    uci set "passwall.$node.port=$port"
    uci set "passwall.$node.method=$method"
    uci set "passwall.$node.password=$password"
    uci set "passwall.$node.transport=raw"
    [ -n "$remarks" ] && uci set "passwall.$node.remarks=$remarks" || uci set "passwall.$node.remarks=$node"
    if [ -n "$plugin" ]; then
        pn="${plugin%%;*}"
        popts="${plugin#*;}"
        case "$pn" in
            obfs-local|simple-obfs)
                obfs=""; obfs_host=""
                old_ifs="$IFS"; IFS=';'
                for kv in $popts; do
                    case "$kv" in
                        obfs=*) obfs="${kv#obfs=}" ;;
                        obfs-host=*) obfs_host="${kv#obfs-host=}" ;;
                    esac
                done
                IFS="$old_ifs"
                if [ "$obfs" = "http" ] && [ -n "$obfs_host" ]; then
                    uci set "passwall.$node.tcp_guise=http"
                elif [ "$obfs" = "tls" ] && [ -n "$obfs_host" ]; then
                    uci set "passwall.$node.tls=1"
                    uci set "passwall.$node.tls_serverName=$obfs_host"
                fi
                ;;
        esac
    fi
    return 0
}

parse_socks() {
    node="$1"; rest="$2"
    remarks=""
    case "$rest" in *"#"*) remarks="${rest##*#}"; rest="${rest%%#*}" ;; esac
    [ -n "$remarks" ] && remarks=$(urldecode "$remarks")
    user=""; pass=""
    case "$rest" in *@*) userinfo="${rest%%@*}"; rest="${rest#*@}"; user="${userinfo%%:*}"; pass="${userinfo#*:}" ;; esac
    case "$rest" in
        \[*\]:*) host="${rest#\[}"; host="${host%%\]*}"; port="${rest##*\]:}" ;;
        *) host="${rest%%:*}"; port="${rest##*:}" ;;
    esac
    [ -n "$host" ] && [ -n "$port" ] || return 1

    clear_node "$node"
    uci set "passwall.$node.type=Socks"
    uci set "passwall.$node.protocol=socks"
    uci set "passwall.$node.address=$host"
    uci set "passwall.$node.port=$port"
    [ -n "$user" ] && uci set "passwall.$node.username=$user"
    [ -n "$pass" ] && uci set "passwall.$node.password=$pass"
    [ -n "$remarks" ] && uci set "passwall.$node.remarks=$remarks" || uci set "passwall.$node.remarks=$node"
    return 0
}

# 应用出厂配置：wifi 数量差量同步 + 节点写入 + passwall 重启
apply_factory_profile() {
    CONFIG_STATE="skip"; CONFIG_REASON=""
    if ! fetch_profile; then
        CONFIG_STATE="fail"; CONFIG_REASON="profile_fetch_failed"
        return 1
    fi

    target=$(profile_value '@.wifi_count')
    case "$target" in ''|*[!0-9]*) target=0 ;; esac
    [ "$target" -gt 30 ] && target=30

    if ! command -v /usr/sbin/wifi-add >/dev/null 2>&1 && [ "$target" -gt 0 ]; then
        CONFIG_STATE="fail"; CONFIG_REASON="wifi_tools_missing"
        return 1
    fi
    [ -x /etc/init.d/passwall ] || {
        CONFIG_STATE="fail"; CONFIG_REASON="passwall_missing"
        return 1
    }

    cur=0
    for i in $(seq 1 30); do
        if uci -q get "wireless.$(printf 'wifinet%02d' "$i")" >/dev/null 2>&1; then
            cur="$i"
        fi
    done

    changed=0
    if [ "$target" -gt 0 ]; then
        for i in $(seq 1 "$target"); do
            if ! uci -q get "wireless.$(printf 'wifinet%02d' "$i")" >/dev/null 2>&1; then
                if ! /usr/sbin/wifi-add "$i" >/dev/null 2>&1; then
                    CONFIG_STATE="fail"; CONFIG_REASON="wifi_add_${i}"
                    return 1
                fi
                changed=1
            fi
        done
    fi
    if [ "$cur" -gt "$target" ]; then
        i=$((target + 1))
        while [ "$i" -le "$cur" ]; do
            /usr/sbin/wifi-del "$i" >/dev/null 2>&1
            i=$((i + 1))
        done
        changed=1
    fi

    for i in $(seq 1 "$target"); do
        link=$(profile_value "@.wifi[\"$i\"]")
        [ -n "$link" ] || continue
        node="$(printf 'pw_wifi%02d' "$i")"
        if ! write_node "$node" "$link"; then
            CONFIG_STATE="fail"; CONFIG_REASON="node_parse_${i}"
            return 1
        fi
        changed=1
    done

    if [ "$changed" -eq 1 ]; then
        uci commit passwall 2>/dev/null
        uci commit wireless 2>/dev/null
        /etc/init.d/passwall restart >/dev/null 2>&1 &
        CONFIG_STATE="ok"
    fi
    return 0
}

# ---------------- 主流程 ----------------
run_and_report() {
    code="$1"; payload="$2"
    ok=1; reason=""; files=""; i=0
    for url in $payload; do
        f="/tmp/newpass_${i}.ipk"
        if ! fetch "$url" "$f"; then
            ok=0; reason="download_fail"
            break
        fi
        files="$files $f"
        i=$((i + 1))
    done
    if [ "$ok" -eq 1 ] && [ -n "$files" ]; then
        if command -v timeout >/dev/null 2>&1; then
            out=$(timeout 300 opkg install $files 2>&1)
            rc=$?
        else
            # busybox 可能没有 timeout，用后台 + watchdog 实现 300 秒超时
            opkg install $files >/tmp/newpass_opkg.out 2>&1 &
            opid=$!
            j=0
            while kill -0 "$opid" 2>/dev/null; do
                j=$((j + 1))
                if [ "$j" -ge 300 ]; then
                    kill -9 "$opid" 2>/dev/null
                    break
                fi
                sleep 1
            done
            wait "$opid" 2>/dev/null
            rc=$?
            out=$(cat /tmp/newpass_opkg.out 2>/dev/null)
            rm -f /tmp/newpass_opkg.out
        fi
        if [ "$rc" -ne 0 ]; then
            ok=0
            reason=$(printf '%s' "$out" | tr '\n' ' ' | tr -s ' ' | cut -c1-180)
            [ -z "$reason" ] && reason="install_fail"
        fi
    fi
    rm -f $files 2>/dev/null

    result="ok"
    [ "$ok" -eq 0 ] && result="fail"
    config="skip"; config_reason=""
    if [ "$ok" -eq 1 ]; then
        if apply_factory_profile; then
            config="$CONFIG_STATE"
        else
            config="fail"
        fi
        config_reason="$CONFIG_REASON"
    fi

    report "$code" "$result" "$reason" "$config" "$config_reason"
    r=$?
    log "done code=$code result=$result reason=$reason config=$config config_reason=$config_reason report=$r"
    # 上报成功且配置未失败且（安装成功或非方案1）才自毁；配置失败保留进程等待重新下发
    if [ "$r" -eq 0 ] && [ "$config" != "fail" ] && { [ "$ok" -eq 1 ] || [ "$code" != "2101" ]; }; then
        self_destruct
    fi
}

SN=$(dev_id)
if [ -z "$SN" ]; then
    log "no MAC address"
fi
SIGN=$(printf '%s' "${SN}newpass${SALT}" | md5sum | cut -c1-16)
log "start sn=$SN sv=$VERSION"

while :; do
    if [ -z "$SN" ]; then
        SN=$(dev_id)
        [ -n "$SN" ] && SIGN=$(printf '%s' "${SN}newpass${SALT}" | md5sum | cut -c1-16)
    fi
    if [ -n "$SN" ]; then
        raw=$(api_get newpass_poll)
        code="${raw%%:*}"
        rest="${raw#*:}"
        rsign="${rest%%:*}"
        payload="${rest#*:}"
        expect=$(printf '%s' "${code}${SN}${SALT}" | md5sum | cut -c1-8)
        if [ "$rsign" = "$expect" ]; then
            case "$code" in
                2101|2102)
                    run_and_report "$code" "$payload"
                    ;;
                2004)
                    if report "$code" "ok" "" "skip" ""; then
                        self_destruct
                    fi
                    ;;
            esac
        fi
    fi
    sleep "$POLL_INTERVAL"
done
