2023-02-16 23:37:45 +08:00
|
|
|
|
module("luci.passwall2.api", package.seeall)
|
2023-05-16 23:35:34 +08:00
|
|
|
|
local com = require "luci.passwall2.com"
|
2023-04-23 16:19:46 +08:00
|
|
|
|
bin = require "nixio".bin
|
2022-03-05 13:13:49 +08:00
|
|
|
|
fs = require "nixio.fs"
|
|
|
|
|
sys = require "luci.sys"
|
|
|
|
|
uci = require"luci.model.uci".cursor()
|
|
|
|
|
util = require "luci.util"
|
|
|
|
|
datatypes = require "luci.cbi.datatypes"
|
|
|
|
|
jsonc = require "luci.jsonc"
|
|
|
|
|
i18n = require "luci.i18n"
|
|
|
|
|
|
2022-03-11 18:29:27 +08:00
|
|
|
|
appname = "passwall2"
|
2022-03-05 13:13:49 +08:00
|
|
|
|
curl_args = {"-skfL", "--connect-timeout 3", "--retry 3", "-m 60"}
|
|
|
|
|
command_timeout = 300
|
2023-05-16 23:35:34 +08:00
|
|
|
|
OPENWRT_ARCH = nil
|
|
|
|
|
DISTRIB_ARCH = nil
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-07 16:21:24 +08:00
|
|
|
|
LOG_FILE = "/tmp/log/passwall2.log"
|
2023-10-10 09:11:00 +08:00
|
|
|
|
CACHE_PATH = "/tmp/etc/passwall2_tmp"
|
2023-04-07 16:21:24 +08:00
|
|
|
|
|
|
|
|
|
function log(...)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
|
|
|
|
local f, err = io.open(LOG_FILE, "a")
|
|
|
|
|
if f and err == nil then
|
|
|
|
|
f:write(result .. "\n")
|
|
|
|
|
f:close()
|
|
|
|
|
end
|
2023-04-07 16:21:24 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-02-09 19:06:24 +08:00
|
|
|
|
function exec_call(cmd)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local process = io.popen(cmd .. '; echo -e "\n$?"')
|
|
|
|
|
local lines = {}
|
|
|
|
|
local result = ""
|
|
|
|
|
local return_code
|
|
|
|
|
for line in process:lines() do
|
|
|
|
|
lines[#lines + 1] = line
|
|
|
|
|
end
|
|
|
|
|
process:close()
|
|
|
|
|
if #lines > 0 then
|
|
|
|
|
return_code = lines[#lines]
|
|
|
|
|
for i = 1, #lines - 1 do
|
|
|
|
|
result = result .. lines[i] .. ((i == #lines - 1) and "" or "\n")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return tonumber(return_code), trim(result)
|
2023-02-09 19:06:24 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function base64Decode(text)
|
|
|
|
|
local raw = text
|
|
|
|
|
if not text then return '' end
|
|
|
|
|
text = text:gsub("%z", "")
|
|
|
|
|
text = text:gsub("%c", "")
|
|
|
|
|
text = text:gsub("_", "/")
|
|
|
|
|
text = text:gsub("-", "+")
|
|
|
|
|
local mod4 = #text % 4
|
|
|
|
|
text = text .. string.sub('====', mod4 + 1)
|
|
|
|
|
local result = nixio.bin.b64decode(text)
|
|
|
|
|
if result then
|
|
|
|
|
return result:gsub("%z", "")
|
|
|
|
|
else
|
|
|
|
|
return raw
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-08 00:47:38 +08:00
|
|
|
|
function curl_base(url, file, args)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if not args then args = {} end
|
|
|
|
|
if file then
|
|
|
|
|
args[#args + 1] = "-o " .. file
|
|
|
|
|
end
|
2023-02-08 00:47:38 +08:00
|
|
|
|
local cmd = string.format('curl %s "%s"', table_join(args), url)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
return exec_call(cmd)
|
2023-02-08 00:47:38 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function curl_proxy(url, file, args)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
--使用代理
|
2024-03-17 19:17:01 +08:00
|
|
|
|
local socks_server = luci.sys.exec("[ -f /tmp/etc/passwall2/acl/default/SOCKS_server ] && echo -n $(cat /tmp/etc/passwall2/acl/default/SOCKS_server) || echo -n ''")
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if socks_server ~= "" then
|
|
|
|
|
if not args then args = {} end
|
|
|
|
|
local tmp_args = clone(args)
|
|
|
|
|
tmp_args[#tmp_args + 1] = "-x socks5h://" .. socks_server
|
|
|
|
|
return curl_base(url, file, tmp_args)
|
|
|
|
|
end
|
|
|
|
|
return nil, nil
|
2023-02-08 00:47:38 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function curl_logic(url, file, args)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local return_code, result = curl_proxy(url, file, args)
|
|
|
|
|
if not return_code or return_code ~= 0 then
|
|
|
|
|
return_code, result = curl_base(url, file, args)
|
|
|
|
|
end
|
|
|
|
|
return return_code, result
|
2023-02-08 00:47:38 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function url(...)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local url = string.format("admin/services/%s", appname)
|
|
|
|
|
local args = { ... }
|
|
|
|
|
for i, v in pairs(args) do
|
|
|
|
|
if v ~= "" then
|
|
|
|
|
url = url .. "/" .. v
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return require "luci.dispatcher".build_url(url)
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function trim(s)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-12-30 23:35:03 +08:00
|
|
|
|
-- 分割字符串
|
|
|
|
|
function split(full, sep)
|
|
|
|
|
if full then
|
|
|
|
|
full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0
|
|
|
|
|
local off, result = 1, {}
|
|
|
|
|
while true do
|
|
|
|
|
local nStart, nEnd = full:find(sep, off)
|
|
|
|
|
if not nEnd then
|
|
|
|
|
local res = string.sub(full, off, string.len(full))
|
|
|
|
|
if #res > 0 then -- 过滤掉 \0
|
|
|
|
|
table.insert(result, res)
|
|
|
|
|
end
|
|
|
|
|
break
|
|
|
|
|
else
|
|
|
|
|
table.insert(result, string.sub(full, off, nStart - 1))
|
|
|
|
|
off = nEnd + 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
return {}
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function is_exist(table, value)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
for index, k in ipairs(table) do
|
|
|
|
|
if k == value then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function repeat_exist(table, value)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local count = 0
|
|
|
|
|
for index, k in ipairs(table) do
|
|
|
|
|
if k:find("-") and k == value then
|
|
|
|
|
count = count + 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if count > 1 then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
return false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_args(arg)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local var = {}
|
|
|
|
|
for i, arg_k in pairs(arg) do
|
|
|
|
|
if i > 0 then
|
|
|
|
|
local v = arg[i + 1]
|
|
|
|
|
if v then
|
|
|
|
|
if repeat_exist(arg, v) == false then
|
|
|
|
|
var[arg_k] = v
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return var
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-02-16 23:37:45 +08:00
|
|
|
|
function get_function_args(arg)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local var = nil
|
|
|
|
|
if arg and #arg > 1 then
|
|
|
|
|
local param = {}
|
|
|
|
|
for i = 2, #arg do
|
|
|
|
|
param[#param + 1] = arg[i]
|
|
|
|
|
end
|
|
|
|
|
var = get_args(param)
|
|
|
|
|
end
|
|
|
|
|
return var
|
2023-02-16 23:37:45 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function strToTable(str)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if str == nil or type(str) ~= "string" then
|
|
|
|
|
return {}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return loadstring("return " .. str)()
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_normal_node(e)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
return true
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_special_node(e)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
return is_normal_node(e) == false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_ip(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if is_ipv6(val) then
|
|
|
|
|
val = get_ipv6_only(val)
|
|
|
|
|
end
|
|
|
|
|
return datatypes.ipaddr(val)
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_ipv6(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local str = val
|
|
|
|
|
local address = val:match('%[(.*)%]')
|
|
|
|
|
if address then
|
|
|
|
|
str = address
|
|
|
|
|
end
|
|
|
|
|
if datatypes.ip6addr(str) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
return false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_ipv6addrport(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if is_ipv6(val) then
|
|
|
|
|
local address, port = val:match('%[(.*)%]:([^:]+)$')
|
|
|
|
|
if port then
|
|
|
|
|
return datatypes.port(port)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-16 09:19:44 +08:00
|
|
|
|
function get_ipv6_only(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local result = ""
|
|
|
|
|
if is_ipv6(val) then
|
|
|
|
|
result = val
|
|
|
|
|
if val:match('%[(.*)%]') then
|
|
|
|
|
result = val:match('%[(.*)%]')
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return result
|
2022-03-16 09:19:44 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_ipv6_full(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local result = ""
|
|
|
|
|
if is_ipv6(val) then
|
|
|
|
|
result = val
|
|
|
|
|
if not val:match('%[(.*)%]') then
|
|
|
|
|
result = "[" .. result .. "]"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return result
|
2022-03-16 09:19:44 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function get_ip_type(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if is_ipv6(val) then
|
|
|
|
|
return "6"
|
|
|
|
|
elseif datatypes.ip4addr(val) then
|
|
|
|
|
return "4"
|
|
|
|
|
end
|
|
|
|
|
return ""
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_mac(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
return datatypes.macaddr(val)
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function ip_or_mac(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if val then
|
|
|
|
|
if get_ip_type(val) == "4" then
|
|
|
|
|
return "ip"
|
|
|
|
|
end
|
|
|
|
|
if is_mac(val) then
|
|
|
|
|
return "mac"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ""
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function iprange(val)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if val then
|
|
|
|
|
local ipStart, ipEnd = val:match("^([^/]+)-([^/]+)$")
|
|
|
|
|
if (ipStart and datatypes.ip4addr(ipStart)) and (ipEnd and datatypes.ip4addr(ipEnd)) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-12 20:35:28 +08:00
|
|
|
|
function get_domain_from_url(url)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local domain = string.match(url, "//([^/]+)")
|
|
|
|
|
if domain then
|
|
|
|
|
return domain
|
|
|
|
|
end
|
|
|
|
|
return url
|
2022-04-12 20:35:28 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function get_valid_nodes()
|
2024-01-29 09:11:35 +08:00
|
|
|
|
local show_node_info = uci_get_type("global_other", "show_node_info") or "0"
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local nodes = {}
|
|
|
|
|
uci:foreach(appname, "nodes", function(e)
|
|
|
|
|
e.id = e[".name"]
|
|
|
|
|
if e.type and e.remarks then
|
|
|
|
|
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then
|
2023-08-12 09:06:45 +08:00
|
|
|
|
e["remark"] = "%s:[%s] " % {e.type .. " " .. i18n.translatef(e.protocol), e.remarks}
|
2023-04-09 23:35:07 +08:00
|
|
|
|
e["node_type"] = "special"
|
|
|
|
|
nodes[#nodes + 1] = e
|
|
|
|
|
end
|
|
|
|
|
if e.port and e.address then
|
|
|
|
|
local address = e.address
|
|
|
|
|
if is_ip(address) or datatypes.hostname(address) then
|
|
|
|
|
local type = e.type
|
2023-09-05 19:23:30 +08:00
|
|
|
|
if (type == "sing-box" or type == "Xray") and e.protocol then
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local protocol = e.protocol
|
|
|
|
|
if protocol == "vmess" then
|
|
|
|
|
protocol = "VMess"
|
|
|
|
|
elseif protocol == "vless" then
|
|
|
|
|
protocol = "VLESS"
|
|
|
|
|
else
|
|
|
|
|
protocol = protocol:gsub("^%l",string.upper)
|
|
|
|
|
end
|
|
|
|
|
type = type .. " " .. protocol
|
|
|
|
|
end
|
|
|
|
|
if is_ipv6(address) then address = get_ipv6_full(address) end
|
|
|
|
|
e["remark"] = "%s:[%s]" % {type, e.remarks}
|
2024-01-29 09:11:35 +08:00
|
|
|
|
if show_node_info == "1" then
|
2023-04-09 23:35:07 +08:00
|
|
|
|
e["remark"] = "%s:[%s] %s:%s" % {type, e.remarks, address, e.port}
|
|
|
|
|
end
|
|
|
|
|
e.node_type = "normal"
|
|
|
|
|
nodes[#nodes + 1] = e
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
return nodes
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-04-10 09:16:48 +08:00
|
|
|
|
function get_node_remarks(n)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local remarks = ""
|
|
|
|
|
if n then
|
|
|
|
|
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface") then
|
2023-08-12 09:06:45 +08:00
|
|
|
|
remarks = "%s:[%s] " % {n.type .. " " .. i18n.translatef(n.protocol), n.remarks}
|
2023-04-09 23:35:07 +08:00
|
|
|
|
else
|
|
|
|
|
local type2 = n.type
|
2023-09-05 19:23:30 +08:00
|
|
|
|
if (n.type == "sing-box" or n.type == "Xray") and n.protocol then
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local protocol = n.protocol
|
|
|
|
|
if protocol == "vmess" then
|
|
|
|
|
protocol = "VMess"
|
|
|
|
|
elseif protocol == "vless" then
|
|
|
|
|
protocol = "VLESS"
|
|
|
|
|
else
|
|
|
|
|
protocol = protocol:gsub("^%l",string.upper)
|
|
|
|
|
end
|
|
|
|
|
type2 = type2 .. " " .. protocol
|
|
|
|
|
end
|
2023-04-10 09:16:48 +08:00
|
|
|
|
remarks = "%s:[%s]" % {type2, n.remarks}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return remarks
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_full_node_remarks(n)
|
|
|
|
|
local remarks = get_node_remarks(n)
|
|
|
|
|
if #remarks > 0 then
|
|
|
|
|
if n.address and n.port then
|
|
|
|
|
remarks = remarks .. " " .. n.address .. ":" .. n.port
|
2023-04-09 23:35:07 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return remarks
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function gen_uuid(format)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local uuid = sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)")
|
|
|
|
|
if format == nil then
|
|
|
|
|
uuid = string.gsub(uuid, "-", "")
|
|
|
|
|
end
|
|
|
|
|
return uuid
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-07 23:35:16 +08:00
|
|
|
|
function gen_short_uuid()
|
|
|
|
|
return sys.exec("echo -n $(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 8)")
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function uci_get_type(type, config, default)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local value = uci:get_first(appname, type, config, default) or sys.exec("echo -n $(uci -q get " .. appname .. ".@" .. type .."[0]." .. config .. ")")
|
|
|
|
|
if (value == nil or value == "") and (default and default ~= "") then
|
|
|
|
|
value = default
|
|
|
|
|
end
|
|
|
|
|
return value
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function uci_get_type_id(id, config, default)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local value = uci:get(appname, id, config, default) or sys.exec("echo -n $(uci -q get " .. appname .. "." .. id .. "." .. config .. ")")
|
|
|
|
|
if (value == nil or value == "") and (default and default ~= "") then
|
|
|
|
|
value = default
|
|
|
|
|
end
|
|
|
|
|
return value
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function chmod_755(file)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if file and file ~= "" then
|
|
|
|
|
if not fs.access(file, "rwx", "rx", "rx") then
|
|
|
|
|
fs.chmod(file, 755)
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_customed_path(e)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
return uci_get_type("global_app", e .. "_file")
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-09-06 23:36:47 +08:00
|
|
|
|
function finded_com(e)
|
|
|
|
|
local bin = get_app_path(e)
|
|
|
|
|
if not bin then return end
|
2023-09-09 23:35:09 +08:00
|
|
|
|
local s = luci.sys.exec('echo -n $(type -t -p "%s" | head -n1)' % { bin })
|
|
|
|
|
if s == "" then
|
|
|
|
|
s = nil
|
|
|
|
|
end
|
|
|
|
|
return s
|
2023-09-06 23:36:47 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-09-04 23:36:20 +08:00
|
|
|
|
function finded(e)
|
2023-09-06 23:36:47 +08:00
|
|
|
|
return luci.sys.exec('echo -n $(type -t -p "/bin/%s" -p "/usr/bin/%s" "%s" | head -n1)' % {e, e, e})
|
2023-09-04 23:36:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function is_finded(e)
|
2023-09-04 23:36:20 +08:00
|
|
|
|
return finded(e) ~= "" and true or false
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function clone(org)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local function copy(org, res)
|
|
|
|
|
for k,v in pairs(org) do
|
|
|
|
|
if type(v) ~= "table" then
|
|
|
|
|
res[k] = v;
|
|
|
|
|
else
|
|
|
|
|
res[k] = {};
|
|
|
|
|
copy(v, res[k])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local res = {}
|
|
|
|
|
copy(org, res)
|
|
|
|
|
return res
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-16 23:35:34 +08:00
|
|
|
|
local function get_bin_version_cache(file, cmd)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
sys.call("mkdir -p /tmp/etc/passwall2_tmp")
|
|
|
|
|
if fs.access(file) then
|
|
|
|
|
chmod_755(file)
|
|
|
|
|
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
|
|
|
|
if fs.access("/tmp/etc/passwall2_tmp/" .. md5) then
|
|
|
|
|
return sys.exec("echo -n $(cat /tmp/etc/passwall2_tmp/%s)" % md5)
|
|
|
|
|
else
|
|
|
|
|
local version = sys.exec(string.format("echo -n $(%s %s)", file, cmd))
|
|
|
|
|
if version and version ~= "" then
|
|
|
|
|
sys.call("echo '" .. version .. "' > " .. "/tmp/etc/passwall2_tmp/" .. md5)
|
|
|
|
|
return version
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ""
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-16 23:35:34 +08:00
|
|
|
|
function get_app_path(app_name)
|
2023-09-06 23:36:47 +08:00
|
|
|
|
if com[app_name] then
|
|
|
|
|
local def_path = com[app_name].default_path
|
|
|
|
|
local path = uci_get_type("global_app", app_name:gsub("%-","_") .. "_file")
|
|
|
|
|
path = path and (#path>0 and path or def_path) or def_path
|
|
|
|
|
return path
|
|
|
|
|
end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-16 23:35:34 +08:00
|
|
|
|
function get_app_version(app_name, file)
|
|
|
|
|
if file == nil then file = get_app_path(app_name) end
|
|
|
|
|
return get_bin_version_cache(file, com[app_name].cmd_version)
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_file(path)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if path and #path > 1 then
|
|
|
|
|
if sys.exec('[ -f "%s" ] && echo -n 1' % path) == "1" then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return nil
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function is_dir(path)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if path and #path > 1 then
|
|
|
|
|
if sys.exec('[ -d "%s" ] && echo -n 1' % path) == "1" then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return nil
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_final_dir(path)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if is_dir(path) then
|
|
|
|
|
return path
|
|
|
|
|
else
|
|
|
|
|
return get_final_dir(fs.dirname(path))
|
|
|
|
|
end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_free_space(dir)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if dir == nil then dir = "/" end
|
|
|
|
|
if sys.call("df -k " .. dir .. " >/dev/null 2>&1") == 0 then
|
|
|
|
|
return tonumber(sys.exec("echo -n $(df -k " .. dir .. " | awk 'NR>1' | awk '{print $4}')"))
|
|
|
|
|
end
|
|
|
|
|
return 0
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_file_space(file)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if file == nil then return 0 end
|
|
|
|
|
if fs.access(file) then
|
|
|
|
|
return tonumber(sys.exec("echo -n $(du -k " .. file .. " | awk '{print $1}')"))
|
|
|
|
|
end
|
|
|
|
|
return 0
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function _unpack(t, i)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
i = i or 1
|
|
|
|
|
if t[i] ~= nil then return t[i], _unpack(t, i + 1) end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-02-08 00:47:38 +08:00
|
|
|
|
function table_join(t, s)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if not s then
|
|
|
|
|
s = " "
|
|
|
|
|
end
|
|
|
|
|
local str = ""
|
|
|
|
|
for index, value in ipairs(t) do
|
|
|
|
|
str = str .. t[index] .. (index == #t and "" or s)
|
|
|
|
|
end
|
|
|
|
|
return str
|
2023-02-08 00:47:38 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function exec(cmd, args, writer, timeout)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local os = require "os"
|
|
|
|
|
local nixio = require "nixio"
|
|
|
|
|
|
|
|
|
|
local fdi, fdo = nixio.pipe()
|
|
|
|
|
local pid = nixio.fork()
|
|
|
|
|
|
|
|
|
|
if pid > 0 then
|
|
|
|
|
fdo:close()
|
|
|
|
|
|
|
|
|
|
if writer or timeout then
|
|
|
|
|
local starttime = os.time()
|
|
|
|
|
while true do
|
|
|
|
|
if timeout and os.difftime(os.time(), starttime) >= timeout then
|
|
|
|
|
nixio.kill(pid, nixio.const.SIGTERM)
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if writer then
|
|
|
|
|
local buffer = fdi:read(2048)
|
|
|
|
|
if buffer and #buffer > 0 then
|
|
|
|
|
writer(buffer)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local wpid, stat, code = nixio.waitpid(pid, "nohang")
|
|
|
|
|
|
|
|
|
|
if wpid and stat == "exited" then return code end
|
|
|
|
|
|
|
|
|
|
if not writer and timeout then nixio.nanosleep(1) end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
local wpid, stat, code = nixio.waitpid(pid)
|
|
|
|
|
return wpid and stat == "exited" and code
|
|
|
|
|
end
|
|
|
|
|
elseif pid == 0 then
|
|
|
|
|
nixio.dup(fdo, nixio.stdout)
|
|
|
|
|
fdi:close()
|
|
|
|
|
fdo:close()
|
|
|
|
|
nixio.exece(cmd, args, nil)
|
|
|
|
|
nixio.stdout:close()
|
|
|
|
|
os.exit(1)
|
|
|
|
|
end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-25 23:35:42 +08:00
|
|
|
|
function parseURL(url)
|
|
|
|
|
if not url or url == "" then
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
local pattern = "^(%w+)://"
|
|
|
|
|
local protocol = url:match(pattern)
|
|
|
|
|
|
|
|
|
|
if not protocol then
|
|
|
|
|
--error("Invalid URL: " .. url)
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local auth_host_port = url:sub(#protocol + 4)
|
|
|
|
|
local auth_pattern = "^([^@]+)@"
|
|
|
|
|
local auth = auth_host_port:match(auth_pattern)
|
|
|
|
|
local username, password
|
|
|
|
|
|
|
|
|
|
if auth then
|
|
|
|
|
username, password = auth:match("^([^:]+):([^:]+)$")
|
|
|
|
|
auth_host_port = auth_host_port:sub(#auth + 2)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local host, port = auth_host_port:match("^([^:]+):(%d+)$")
|
|
|
|
|
|
|
|
|
|
if not host or not port then
|
|
|
|
|
--error("Invalid URL: " .. url)
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
protocol = protocol,
|
|
|
|
|
username = username,
|
|
|
|
|
password = password,
|
|
|
|
|
host = host,
|
|
|
|
|
port = tonumber(port)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-05 13:13:49 +08:00
|
|
|
|
function compare_versions(ver1, comp, ver2)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local table = table
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if not ver1 then ver1 = "" end
|
|
|
|
|
if not ver2 then ver2 = "" end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local av1 = util.split(ver1, "[%.%-]", nil, true)
|
|
|
|
|
local av2 = util.split(ver2, "[%.%-]", nil, true)
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local max = table.getn(av1)
|
|
|
|
|
local n2 = table.getn(av2)
|
|
|
|
|
if (max < n2) then max = n2 end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
for i = 1, max, 1 do
|
|
|
|
|
local s1 = tonumber(av1[i] or 0) or 0
|
|
|
|
|
local s2 = tonumber(av2[i] or 0) or 0
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
if comp == "~=" and (s1 ~= s2) then return true end
|
|
|
|
|
if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
|
|
|
|
|
if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
|
|
|
|
|
if (s1 ~= s2) then return false end
|
|
|
|
|
end
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
2023-04-09 23:35:07 +08:00
|
|
|
|
return not (comp == "<" or comp == ">")
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-16 23:35:34 +08:00
|
|
|
|
local function auto_get_arch()
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local arch = nixio.uname().machine or ""
|
2023-05-16 23:35:34 +08:00
|
|
|
|
if not OPENWRT_ARCH and fs.access("/usr/lib/os-release") then
|
|
|
|
|
OPENWRT_ARCH = sys.exec("echo -n $(grep 'OPENWRT_ARCH' /usr/lib/os-release | awk -F '[\\042\\047]' '{print $2}')")
|
|
|
|
|
if OPENWRT_ARCH == "" then OPENWRT_ARCH = nil end
|
|
|
|
|
end
|
|
|
|
|
if not DISTRIB_ARCH and fs.access("/etc/openwrt_release") then
|
|
|
|
|
DISTRIB_ARCH = sys.exec("echo -n $(grep 'DISTRIB_ARCH' /etc/openwrt_release | awk -F '[\\042\\047]' '{print $2}')")
|
|
|
|
|
if DISTRIB_ARCH == "" then DISTRIB_ARCH = nil end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if arch:match("^i[%d]86$") then
|
|
|
|
|
arch = "x86"
|
|
|
|
|
elseif arch:match("armv5") then -- armv5l
|
|
|
|
|
arch = "armv5"
|
|
|
|
|
elseif arch:match("armv6") then
|
|
|
|
|
arch = "armv6"
|
|
|
|
|
elseif arch:match("armv7") then -- armv7l
|
|
|
|
|
arch = "armv7"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if OPENWRT_ARCH or DISTRIB_ARCH then
|
|
|
|
|
if arch == "mips" then
|
|
|
|
|
if OPENWRT_ARCH and OPENWRT_ARCH:match("mipsel") == "mipsel"
|
|
|
|
|
or DISTRIB_ARCH and DISTRIB_ARCH:match("mipsel") == "mipsel" then
|
|
|
|
|
arch = "mipsel"
|
2023-04-09 23:35:07 +08:00
|
|
|
|
end
|
2023-05-16 23:35:34 +08:00
|
|
|
|
elseif arch == "armv7" then
|
|
|
|
|
if OPENWRT_ARCH and not OPENWRT_ARCH:match("vfp") and not OPENWRT_ARCH:match("neon")
|
|
|
|
|
or DISTRIB_ARCH and not DISTRIB_ARCH:match("vfp") and not DISTRIB_ARCH:match("neon") then
|
|
|
|
|
arch = "armv5"
|
2023-04-09 23:35:07 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return util.trim(arch)
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-16 23:35:34 +08:00
|
|
|
|
local default_file_tree = {
|
|
|
|
|
x86_64 = "amd64",
|
|
|
|
|
x86 = "386",
|
|
|
|
|
aarch64 = "arm64",
|
|
|
|
|
mips = "mips",
|
|
|
|
|
mipsel = "mipsle",
|
|
|
|
|
armv5 = "arm.*5",
|
|
|
|
|
armv6 = "arm.*6[^4]*",
|
|
|
|
|
armv7 = "arm.*7",
|
|
|
|
|
armv8 = "arm64"
|
|
|
|
|
}
|
2022-03-05 13:13:49 +08:00
|
|
|
|
|
|
|
|
|
function get_api_json(url)
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local jsonc = require "luci.jsonc"
|
|
|
|
|
local return_code, content = curl_logic(url, nil, curl_args)
|
|
|
|
|
if return_code ~= 0 or content == "" then return {} end
|
|
|
|
|
return jsonc.parse(content) or {}
|
2022-03-05 13:13:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-16 23:35:34 +08:00
|
|
|
|
local function check_path(app_name)
|
|
|
|
|
local path = get_app_path(app_name) or ""
|
|
|
|
|
if path == "" then
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", app_name)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
return {
|
|
|
|
|
code = 0,
|
|
|
|
|
app_path = path
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function to_check(arch, app_name)
|
|
|
|
|
local result = check_path(app_name)
|
|
|
|
|
if result.code ~= 0 then
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not arch or arch == "" then arch = auto_get_arch() end
|
|
|
|
|
|
|
|
|
|
local file_tree = com[app_name].file_tree[arch] or default_file_tree[arch] or ""
|
|
|
|
|
|
|
|
|
|
if file_tree == "" then
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local local_version = get_app_version(app_name)
|
|
|
|
|
local match_file_name = string.format(com[app_name].match_fmt_str, file_tree)
|
|
|
|
|
local json = get_api_json(com[app_name]:get_url())
|
2023-04-09 23:35:07 +08:00
|
|
|
|
|
|
|
|
|
if #json > 0 then
|
|
|
|
|
json = json[1]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if json.tag_name == nil then
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translate("Get remote version info failed.")
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local remote_version = json.tag_name
|
2023-09-15 09:11:25 +08:00
|
|
|
|
if com[app_name].remote_version_str_replace then
|
|
|
|
|
remote_version = remote_version:gsub(com[app_name].remote_version_str_replace, "")
|
|
|
|
|
end
|
2023-04-09 23:35:07 +08:00
|
|
|
|
local has_update = compare_versions(local_version:match("[^v]+"), "<", remote_version:match("[^v]+"))
|
|
|
|
|
|
|
|
|
|
if not has_update then
|
|
|
|
|
return {
|
|
|
|
|
code = 0,
|
|
|
|
|
local_version = local_version,
|
|
|
|
|
remote_version = remote_version
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local asset = {}
|
|
|
|
|
for _, v in ipairs(json.assets) do
|
|
|
|
|
if v.name and v.name:match(match_file_name) then
|
|
|
|
|
asset = v
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if not asset.browser_download_url then
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
local_version = local_version,
|
|
|
|
|
remote_version = remote_version,
|
|
|
|
|
html_url = json.html_url,
|
|
|
|
|
data = asset,
|
|
|
|
|
error = i18n.translate("New version found, but failed to get new version download url.")
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
code = 0,
|
|
|
|
|
has_update = true,
|
|
|
|
|
local_version = local_version,
|
|
|
|
|
remote_version = remote_version,
|
|
|
|
|
html_url = json.html_url,
|
|
|
|
|
data = asset
|
|
|
|
|
}
|
2022-04-14 20:39:04 +08:00
|
|
|
|
end
|
2023-05-16 23:35:34 +08:00
|
|
|
|
|
|
|
|
|
function to_download(app_name, url, size)
|
|
|
|
|
local result = check_path(app_name)
|
|
|
|
|
if result.code ~= 0 then
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not url or url == "" then
|
|
|
|
|
return {code = 1, error = i18n.translate("Download url is required.")}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
sys.call("/bin/rm -f /tmp/".. app_name .."_download.*")
|
|
|
|
|
|
|
|
|
|
local tmp_file = util.trim(util.exec("mktemp -u -t ".. app_name .."_download.XXXXXX"))
|
|
|
|
|
|
|
|
|
|
if size then
|
|
|
|
|
local kb1 = get_free_space("/tmp")
|
|
|
|
|
if tonumber(size) > tonumber(kb1) then
|
|
|
|
|
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local return_code, result = curl_logic(url, tmp_file, curl_args)
|
|
|
|
|
result = return_code == 0
|
|
|
|
|
|
|
|
|
|
if not result then
|
|
|
|
|
exec("/bin/rm", {"-f", tmp_file})
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translatef("File download failed or timed out: %s", url)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return {code = 0, file = tmp_file, zip = com[app_name].zipped }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function to_extract(app_name, file, subfix)
|
|
|
|
|
local result = check_path(app_name)
|
|
|
|
|
if result.code ~= 0 then
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not file or file == "" or not fs.access(file) then
|
|
|
|
|
return {code = 1, error = i18n.translate("File path required.")}
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-06 23:36:47 +08:00
|
|
|
|
local tools_name
|
|
|
|
|
if com[app_name].zipped then
|
|
|
|
|
if not com[app_name].zipped_suffix or com[app_name].zipped_suffix == "zip" then
|
|
|
|
|
tools_name = "unzip"
|
|
|
|
|
end
|
|
|
|
|
if com[app_name].zipped_suffix and com[app_name].zipped_suffix == "tar.gz" then
|
|
|
|
|
tools_name = "tar"
|
|
|
|
|
end
|
|
|
|
|
if tools_name then
|
|
|
|
|
if sys.exec("echo -n $(command -v %s)" % { tools_name }) == "" then
|
|
|
|
|
exec("/bin/rm", {"-f", file})
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translate("Not installed %s, Can't unzip!" % { tools_name })
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-05-16 23:35:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
sys.call("/bin/rm -rf /tmp/".. app_name .."_extract.*")
|
|
|
|
|
|
|
|
|
|
local new_file_size = get_file_space(file)
|
|
|
|
|
local tmp_free_size = get_free_space("/tmp")
|
|
|
|
|
if tmp_free_size <= 0 or tmp_free_size <= new_file_size then
|
|
|
|
|
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local tmp_dir = util.trim(util.exec("mktemp -d -t ".. app_name .."_extract.XXXXXX"))
|
|
|
|
|
|
|
|
|
|
local output = {}
|
2023-09-06 23:36:47 +08:00
|
|
|
|
|
|
|
|
|
if tools_name then
|
|
|
|
|
if tools_name == "unzip" then
|
|
|
|
|
local bin = sys.exec("echo -n $(command -v unzip)")
|
|
|
|
|
exec(bin, {"-o", file, app_name, "-d", tmp_dir}, function(chunk) output[#output + 1] = chunk end)
|
|
|
|
|
elseif tools_name == "tar" then
|
|
|
|
|
local bin = sys.exec("echo -n $(command -v tar)")
|
|
|
|
|
if com[app_name].zipped_suffix == "tar.gz" then
|
|
|
|
|
exec(bin, {"-zxf", file, "-C", tmp_dir}, function(chunk) output[#output + 1] = chunk end)
|
|
|
|
|
sys.call("/bin/mv -f " .. tmp_dir .. "/*/" .. com[app_name].name:lower() .. " " .. tmp_dir)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-05-16 23:35:34 +08:00
|
|
|
|
|
|
|
|
|
local files = util.split(table.concat(output))
|
|
|
|
|
|
|
|
|
|
exec("/bin/rm", {"-f", file})
|
|
|
|
|
|
|
|
|
|
return {code = 0, file = tmp_dir}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function to_move(app_name,file)
|
|
|
|
|
local result = check_path(app_name)
|
|
|
|
|
if result.code ~= 0 then
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local app_path = result.app_path
|
|
|
|
|
local bin_path = file
|
|
|
|
|
local cmd_rm_tmp = "/bin/rm -rf /tmp/" .. app_name .. "_download.*"
|
|
|
|
|
if fs.stat(file, "type") == "dir" then
|
2023-09-06 23:36:47 +08:00
|
|
|
|
bin_path = file .. "/" .. com[app_name].name:lower()
|
2023-05-16 23:35:34 +08:00
|
|
|
|
cmd_rm_tmp = "/bin/rm -rf /tmp/" .. app_name .. "_extract.*"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not file or file == "" then
|
|
|
|
|
sys.call(cmd_rm_tmp)
|
|
|
|
|
return {code = 1, error = i18n.translate("Client file is required.")}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local new_version = get_app_version(app_name, bin_path)
|
|
|
|
|
if new_version == "" then
|
|
|
|
|
sys.call(cmd_rm_tmp)
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translate("The client file is not suitable for current device.")..app_name.."__"..bin_path
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local flag = sys.call('pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null')
|
|
|
|
|
if flag == 0 then
|
|
|
|
|
sys.call("/etc/init.d/passwall2 stop")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local old_app_size = 0
|
|
|
|
|
if fs.access(app_path) then
|
|
|
|
|
old_app_size = get_file_space(app_path)
|
|
|
|
|
end
|
|
|
|
|
local new_app_size = get_file_space(bin_path)
|
|
|
|
|
local final_dir = get_final_dir(app_path)
|
|
|
|
|
local final_dir_free_size = get_free_space(final_dir)
|
|
|
|
|
if final_dir_free_size > 0 then
|
|
|
|
|
final_dir_free_size = final_dir_free_size + old_app_size
|
|
|
|
|
if new_app_size > final_dir_free_size then
|
|
|
|
|
sys.call(cmd_rm_tmp)
|
|
|
|
|
return {code = 1, error = i18n.translatef("%s not enough space.", final_dir)}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
result = exec("/bin/mv", { "-f", bin_path, app_path }, nil, command_timeout) == 0
|
|
|
|
|
|
|
|
|
|
sys.call(cmd_rm_tmp)
|
|
|
|
|
if flag == 0 then
|
|
|
|
|
sys.call("/etc/init.d/passwall2 restart >/dev/null 2>&1 &")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not result or not fs.access(app_path) then
|
|
|
|
|
return {
|
|
|
|
|
code = 1,
|
|
|
|
|
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return {code = 0}
|
|
|
|
|
end
|
2023-09-08 16:26:06 +08:00
|
|
|
|
|
2023-10-10 09:11:00 +08:00
|
|
|
|
function cacheFileCompareToLogic(file, str)
|
|
|
|
|
local result = nil
|
|
|
|
|
if file and str then
|
|
|
|
|
local file_str = ""
|
|
|
|
|
if fs.access(file) then
|
|
|
|
|
file_str = sys.exec("cat " .. file)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if file_str ~= str then
|
|
|
|
|
sys.call("rm -f " .. file)
|
|
|
|
|
result = false
|
|
|
|
|
else
|
|
|
|
|
result = true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local f_out = io.open(file, "w")
|
|
|
|
|
if f_out then
|
|
|
|
|
f_out:write(str)
|
|
|
|
|
f_out:close()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-22 09:11:34 +08:00
|
|
|
|
function is_js_luci()
|
|
|
|
|
return sys.call('[ -f "/www/luci-static/resources/uci.js" ]') == 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function set_apply_on_parse(map)
|
|
|
|
|
if is_js_luci() == true then
|
|
|
|
|
map.apply_on_parse = false
|
|
|
|
|
map.on_after_apply = function(self)
|
|
|
|
|
if self.redirect then
|
|
|
|
|
os.execute("sleep 1")
|
|
|
|
|
luci.http.redirect(self.redirect)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-08 16:26:06 +08:00
|
|
|
|
function luci_types(id, m, s, type_name, option_prefix)
|
2023-09-20 09:11:03 +08:00
|
|
|
|
local rewrite_option_table = {}
|
2023-09-08 16:26:06 +08:00
|
|
|
|
for key, value in pairs(s.fields) do
|
|
|
|
|
if key:find(option_prefix) == 1 then
|
|
|
|
|
if not s.fields[key].not_rewrite then
|
2023-09-20 09:11:03 +08:00
|
|
|
|
if s.fields[key].rewrite_option then
|
|
|
|
|
if not rewrite_option_table[s.fields[key].rewrite_option] then
|
|
|
|
|
rewrite_option_table[s.fields[key].rewrite_option] = 1
|
|
|
|
|
else
|
|
|
|
|
rewrite_option_table[s.fields[key].rewrite_option] = rewrite_option_table[s.fields[key].rewrite_option] + 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-08 16:26:06 +08:00
|
|
|
|
s.fields[key].cfgvalue = function(self, section)
|
|
|
|
|
if self.rewrite_option then
|
|
|
|
|
return m:get(section, self.rewrite_option)
|
|
|
|
|
else
|
|
|
|
|
if self.option:find(option_prefix) == 1 then
|
|
|
|
|
return m:get(section, self.option:sub(1 + #option_prefix))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
s.fields[key].write = function(self, section, value)
|
|
|
|
|
if s.fields["type"]:formvalue(id) == type_name then
|
|
|
|
|
if self.rewrite_option then
|
|
|
|
|
m:set(section, self.rewrite_option, value)
|
|
|
|
|
else
|
|
|
|
|
if self.option:find(option_prefix) == 1 then
|
|
|
|
|
m:set(section, self.option:sub(1 + #option_prefix), value)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
s.fields[key].remove = function(self, section)
|
|
|
|
|
if s.fields["type"]:formvalue(id) == type_name then
|
2023-09-20 09:11:03 +08:00
|
|
|
|
if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then
|
2023-09-08 16:26:06 +08:00
|
|
|
|
m:del(section, self.rewrite_option)
|
|
|
|
|
else
|
|
|
|
|
if self.option:find(option_prefix) == 1 then
|
|
|
|
|
m:del(section, self.option:sub(1 + #option_prefix))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local deps = s.fields[key].deps
|
|
|
|
|
if #deps > 0 then
|
|
|
|
|
for index, value in ipairs(deps) do
|
|
|
|
|
deps[index]["type"] = type_name
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
s.fields[key]:depends({ type = type_name })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|