#!/opt/lnxall_app/bin/lua

-- Created by jiaqiang.ye@lnxall.com
-- Simple WiFi configuration script
-- 2026/04/20

local uci           = require "uci"
local invoker       = require "invoker"
local gfmt          = string.format

local g_wifiwan     = "wifiwan"
local g_etccfg      = nil
local g_ssid        = nil
local g_metric      = 30
local g_passwd      = nil
local g_actions     = {}
local g_lockfd      = nil

local function wificfg_help()
	io.stderr:write("Usage: wificfg dis # 禁用WiFi功能\n")
	io.stderr:write("       wificfg sta|ap --ssid SSID --metric METRIC [--passwd PASSWORD]\n")
	io.stderr:flush()
end

local function goodstr(s, cha)
	if type(s) ~= "string" then return false end
	local len = string.len(s)
	if len == 0 then return false end
	if cha and string.find(s, cha, 1, true) then
		io.stderr:write(gfmt("Error, invalid character found in %s: %s\n", s, cha))
		io.stderr:flush()
		return false
	end
	return len
end

local function enable_wifiwan(enable, metric, which)
	if not which then which = "wifiwan" end
	local netw = uci.cursor(g_etccfg)
	local netcfg = netw:get('network', which)
	if netcfg ~= "interface" then
		local ncfg = gfmt("%s/network", g_etccfg)
		io.stderr:write(gfmt("Warning, network not found in %s: %s\n", ncfg, which))
		io.stderr:flush()

		local fp = io.open(ncfg, "ab")
		if fp then
			fp:write(gfmt([[

config interface 'wifiwan'
	option proto 'dhcp'
	option device 'wlan0'
	option metric '%d'
	option disabled '%s'
]], metric, enable and "0" or "1"))
			fp:close(); fp = nil
		end
		return true
	end

	local commit = nil
	local dis = netw:get('network', which, 'disabled')
	local met = netw:get('network', which, 'metric')
	if enable and tonumber(met) ~= metric then
		commit = true
		netw:set("network", which, "metric", tostring(metric))
	end

	if enable and dis ~= "0" then
		commit = true
		netw:set("network", which, "disabled", "0")
	end

	if dis ~= "1" and not enable then
		commit = true
		netw:set("network", which, "disabled", "1")
	end

	if commit then
		io.stdout:write("Updating network for wifiwan...\n")
		io.stdout:flush()
		netw:commit('network')
	end
	netw:close(); netw = nil
	return true
end

g_actions["sta"] = function ()
	if not g_ssid then
		wificfg_help()
		return false
	end

	invoker.unlink(g_etccfg .. "/wireless")
	invoker.invoke(0, "wifi", "config")
	for k = 1, 2 do
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.radio0.disabled")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.ssid")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.encryption")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.key")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.mode")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.network")
	end

	invoker.invoke(0, "uci", "set", "wireless.radio0.disabled=0")
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.mode=sta")
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.disabled=0")
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.ssid=" .. g_ssid)
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.network=wifiwan")
	if g_passwd then
		invoker.invoke(0, "uci", "set", "wireless.default_radio0.encryption=psk2")
		invoker.invoke(0, "uci", "set", "wireless.default_radio0.key=" .. g_passwd)
	else
		invoker.invoke(0, "uci", "set", "wireless.default_radio0.encryption=none")
	end
	invoker.invoke(0, "uci", "commit", "wireless")
	return enable_wifiwan(true, g_metric)
end

g_actions["ap"] = function ()
	if not g_ssid then
		wificfg_help()
		return false
	end

	invoker.unlink(g_etccfg .. "/wireless")
	invoker.invoke(0, "wifi", "config")
	for k = 1, 2 do
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.radio0.disabled")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.ssid")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.encryption")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.key")
		invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.default_radio0.mode")
	end
	invoker.invoke(0, "uci", "set", "wireless.radio0.disabled=0")
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.mode=ap")
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.ssid=" .. g_ssid)
	invoker.invoke(0, "uci", "set", "wireless.default_radio0.disabled=0")
	if g_passwd then
		invoker.invoke(0, "uci", "set", "wireless.default_radio0.encryption=psk2")
		invoker.invoke(0, "uci", "set", "wireless.default_radio0.key=" .. g_passwd)
	else
		invoker.invoke(0, "uci", "set", "wireless.default_radio0.encryption=none")
	end
	invoker.invoke(0, "uci", "commit", "wireless")

	return enable_wifiwan(false, g_metric)
end

g_actions["dis"] = function ()
	local okay, output = invoker.invoke(invoker.OUTPUT + invoker.TRIMEND,
		"uci", "get", "wireless.radio0.disabled")
	if okay ~= 0 or output ~= "1" then
		for k = 1, 2 do
			invoker.invoke(invoker.NOSTDIO, "uci", "del", "wireless.radio0.disabled")
		end
		invoker.invoke(0, "uci", "set", "wireless.radio0.disabled=1")
		invoker.invoke(0, "uci", "commit", "wireless")
	end
	return enable_wifiwan(false)
end

local function mainfunc()
	local cfgpath = "/etc/config"
	if invoker.statfile(cfgpath) ~= "directory" then
		io.stderr:write(gfmt("Error, configuration directory not found: %s\n", cfgpath))
		io.stderr:flush()
		return false
	end
	g_etccfg = cfgpath

	local arg1, arg2, action = arg[1], nil, nil
	if not arg1 or #arg1 == 0 then
		action = "WHAT-THE-FUCK"
	else
		action = arg1
	end
	local action = g_actions[action]
	if not action then
		io.stderr:write("Error, invalid command-line arguments.\n")
		io.stderr:flush()
		wificfg_help()
		return false
	end

	local argc = 0
	while true do
		argc = argc + 2
		arg1, arg2 = arg[argc], arg[argc + 1]
		if not (arg1 and arg2) then break end

		if arg1 == "--ssid" and goodstr(arg2, "'") then
			g_ssid = arg2
		elseif arg1 == "--metric" and goodstr(arg2) then
			local metric = tonumber(arg2)
			if type(metric) == "number" and metric >= 0 then
				g_metric = math.floor(metric)
			end
		elseif arg1 == "--passwd" and goodstr(arg2, "'") then
			g_passwd = arg2
		end
	end
	return action()
end

g_lockfd = invoker.waitlock('/tmp/.netuci-lock')
if g_lockfd then invoker.cloexec(g_lockfd, true) end
os.exit(mainfunc() and 0 or 1)
