mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-31 22:25:13 +08:00
🎄 Sync 2026-05-06 19:35:12
This commit is contained in:
1 parent
b3aea13cdd
commit
f58b6dd3a1
50 files changed
+1787
-626
No files matched your search
@@ -0,0 +1,486 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
-- Copyright(c): lwb1978 2025–2027
|
||||
|
||||
local api = require("luci.passwall.api")
|
||||
local urlencode = api.UrlEncode
|
||||
local base64 = api.base64Encode
|
||||
local json = api.jsonc
|
||||
|
||||
local function host_format(host)
|
||||
if not host then return "" end
|
||||
local str = host:match("%[(.-)%]") or host
|
||||
if api.datatypes.ip6addr(str) then
|
||||
return "[" .. str .. "]"
|
||||
end
|
||||
return host
|
||||
end
|
||||
|
||||
local function build_alpn(alpn) -- 排序+去重
|
||||
if not alpn then return nil end
|
||||
|
||||
local seen = {}
|
||||
local order = { "h3", "h2", "http/1.1" }
|
||||
|
||||
if type(alpn) == "table" then
|
||||
for _, v in ipairs(alpn) do
|
||||
if v then seen[v] = true end
|
||||
end
|
||||
else
|
||||
seen[tostring(alpn)] = true
|
||||
end
|
||||
|
||||
local t = {}
|
||||
for _, v in ipairs(order) do
|
||||
if seen[v] then table.insert(t, v) end
|
||||
end
|
||||
|
||||
if #t == 0 then return nil end
|
||||
return table.concat(t, ",")
|
||||
end
|
||||
|
||||
local function build_common(node)
|
||||
local o = {}
|
||||
|
||||
o.server = host_format(node.server)
|
||||
o.port = node.port
|
||||
o.name = node.name
|
||||
|
||||
-- ===== TLS =====
|
||||
o.tls = {}
|
||||
|
||||
if node["reality-opts"] then
|
||||
o.tls.security = "reality"
|
||||
o.tls.pbk = node["reality-opts"]["public-key"]
|
||||
o.tls.sid = node["reality-opts"]["short-id"]
|
||||
elseif node.tls then
|
||||
o.tls.security = "tls"
|
||||
end
|
||||
|
||||
o.tls.sni = node.servername or node.sni
|
||||
o.tls.fp = node["client-fingerprint"]
|
||||
o.tls.pcs = node.fingerprint
|
||||
o.tls.insecure = node["skip-cert-verify"] == true
|
||||
|
||||
o.tls.alpn = build_alpn(node.alpn)
|
||||
|
||||
local ech_opts = node["ech-opts"]
|
||||
if ech_opts and ech_opts.enable == true then
|
||||
if ech_opts.config then
|
||||
o.tls.ech = ech_opts.config
|
||||
elseif ech_opts["query-server-name"] then
|
||||
o.tls.ech = ech_opts["query-server-name"] .. "+https://223.5.5.5/dns-query"
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== transport =====
|
||||
o.transport = {}
|
||||
local net = node.network or "tcp"
|
||||
o.transport.type = net
|
||||
|
||||
local function get_first(v)
|
||||
if type(v) == "table" then return v[1] end
|
||||
return v
|
||||
end
|
||||
|
||||
if net == "ws" then
|
||||
local opts = node["ws-opts"]
|
||||
if opts then
|
||||
o.transport.path = opts.path
|
||||
o.transport.host = opts.headers and opts.headers.Host
|
||||
end
|
||||
|
||||
elseif net == "grpc" then
|
||||
local opts = node["grpc-opts"]
|
||||
if opts then
|
||||
o.transport.serviceName = opts["grpc-service-name"]
|
||||
end
|
||||
|
||||
elseif net == "http" then
|
||||
local opts = node["http-opts"]
|
||||
if opts then
|
||||
o.transport.host = get_first(opts.host)
|
||||
o.transport.path = get_first(opts.path)
|
||||
end
|
||||
|
||||
elseif net == "h2" then
|
||||
local opts = node["h2-opts"]
|
||||
if opts then
|
||||
o.transport.host = get_first(opts.host)
|
||||
o.transport.path = opts.path
|
||||
end
|
||||
|
||||
elseif net == "xhttp" then
|
||||
local opts = node["xhttp-opts"]
|
||||
if opts then
|
||||
o.transport.host = opts.host
|
||||
o.transport.path = opts.path
|
||||
o.transport.mode = opts.mode
|
||||
|
||||
local extra = {}
|
||||
|
||||
-- headers
|
||||
if opts.headers then
|
||||
extra.headers = opts.headers
|
||||
end
|
||||
|
||||
if opts["x-padding-bytes"] then
|
||||
extra.xPaddingBytes = opts["x-padding-bytes"]
|
||||
end
|
||||
|
||||
if opts["no-grpc-header"] ~= nil then
|
||||
extra.noGRPCHeader = opts["no-grpc-header"]
|
||||
end
|
||||
|
||||
if opts["sc-max-each-post-bytes"] then
|
||||
extra.scMaxEachPostBytes = opts["sc-max-each-post-bytes"]
|
||||
end
|
||||
|
||||
if opts["sc-min-posts-interval-ms"] then
|
||||
extra.scMinPostsIntervalMs = opts["sc-min-posts-interval-ms"]
|
||||
end
|
||||
|
||||
-- xmux
|
||||
if opts["reuse-settings"] then
|
||||
local r = opts["reuse-settings"]
|
||||
local xmux = {}
|
||||
|
||||
if r["max-concurrency"] then xmux.maxConcurrency = r["max-concurrency"] end
|
||||
if r["max-connections"] then xmux.maxConnections = tonumber(r["max-connections"]) end
|
||||
if r["c-max-reuse-times"] then xmux.cMaxReuseTimes = tonumber(r["c-max-reuse-times"]) end
|
||||
if r["h-max-request-times"] then xmux.hMaxRequestTimes = r["h-max-request-times"] end
|
||||
if r["h-max-reusable-secs"] then xmux.hMaxReusableSecs = r["h-max-reusable-secs"] end
|
||||
if r["h-keep-alive-period"] then xmux.hKeepAlivePeriod = tonumber(r["h-keep-alive-period"]) end
|
||||
|
||||
if next(xmux) then extra.xmux = xmux end
|
||||
end
|
||||
|
||||
-- download-settings
|
||||
if opts["download-settings"] then
|
||||
local d = opts["download-settings"]
|
||||
local ds = {}
|
||||
|
||||
if d.server then ds.address = d.server end
|
||||
if d.port then ds.port = d.port end
|
||||
|
||||
ds.network = "xhttp"
|
||||
|
||||
-- TLS
|
||||
if d.tls then
|
||||
ds.security = "tls"
|
||||
ds.tlsSettings = {}
|
||||
|
||||
if d.servername then
|
||||
ds.tlsSettings.serverName = d.servername
|
||||
end
|
||||
if d["skip-cert-verify"] == true then
|
||||
ds.tlsSettings.allowInsecure = true
|
||||
end
|
||||
if d["client-fingerprint"] then
|
||||
ds.tlsSettings.fingerprint = d["client-fingerprint"]
|
||||
end
|
||||
if d.fingerprint then
|
||||
ds.tlsSettings.pinnedPeerCertSha256 = d.fingerprint
|
||||
end
|
||||
if d.alpn then
|
||||
ds.tlsSettings.alpn = d.alpn
|
||||
end
|
||||
end
|
||||
|
||||
-- xhttpSettings
|
||||
local xs = {}
|
||||
|
||||
if d.path then xs.path = d.path end
|
||||
if d.host then xs.host = d.host end
|
||||
|
||||
if next(xs) then
|
||||
ds.xhttpSettings = xs
|
||||
end
|
||||
|
||||
extra.downloadSettings = ds
|
||||
end
|
||||
|
||||
if next(extra) then
|
||||
o.transport.extra = json.stringify(extra)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return o
|
||||
end
|
||||
|
||||
-- VLESS
|
||||
local function encode_vless(node)
|
||||
local o = build_common(node)
|
||||
|
||||
local link = "vless://" .. node.uuid .. "@" .. o.server .. ":" .. o.port
|
||||
local p = {}
|
||||
|
||||
if node.flow then table.insert(p, "flow=" .. urlencode(node.flow)) end
|
||||
if node.encryption then table.insert(p, "encryption=" .. urlencode(node.encryption)) end
|
||||
|
||||
-- TLS
|
||||
if o.tls.security then table.insert(p, "security=" .. o.tls.security) end
|
||||
if o.tls.pbk then table.insert(p, "pbk=" .. urlencode(o.tls.pbk)) end
|
||||
if o.tls.sid then table.insert(p, "sid=" .. urlencode(o.tls.sid)) end
|
||||
if o.tls.sni then table.insert(p, "sni=" .. urlencode(o.tls.sni)) end
|
||||
if o.tls.fp then table.insert(p, "fp=" .. urlencode(o.tls.fp)) end
|
||||
if o.tls.alpn then table.insert(p, "alpn=" .. urlencode(o.tls.alpn)) end
|
||||
if o.tls.ech then table.insert(p, "ech=" .. urlencode(o.tls.ech)) end
|
||||
if o.tls.pcs then table.insert(p, "pcs=" .. urlencode(o.tls.pcs)) end
|
||||
table.insert(p, "allowInsecure=" .. (o.tls.insecure and "1" or "0"))
|
||||
|
||||
-- transport
|
||||
table.insert(p, "type=" .. o.transport.type)
|
||||
if o.transport.host then table.insert(p, "host=" .. urlencode(o.transport.host)) end
|
||||
if o.transport.path then table.insert(p, "path=" .. urlencode(o.transport.path)) end
|
||||
if o.transport.serviceName then table.insert(p, "serviceName=" .. urlencode(o.transport.serviceName)) end
|
||||
if o.transport.mode then table.insert(p, "mode=" .. urlencode(o.transport.mode)) end
|
||||
if o.transport.extra then table.insert(p, "extra=" .. urlencode(o.transport.extra)) end
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(o.name or "")
|
||||
end
|
||||
|
||||
-- Trojan
|
||||
local function encode_trojan(node)
|
||||
local o = build_common(node)
|
||||
|
||||
local link = "trojan://" .. node.password .. "@" .. o.server .. ":" .. o.port
|
||||
local p = {}
|
||||
|
||||
if o.tls.security then table.insert(p, "security=" .. o.tls.security) end
|
||||
if o.tls.pbk then table.insert(p, "pbk=" .. urlencode(o.tls.pbk)) end
|
||||
if o.tls.sid then table.insert(p, "sid=" .. urlencode(o.tls.sid)) end
|
||||
if o.tls.sni then table.insert(p, "sni=" .. urlencode(o.tls.sni)) end
|
||||
if o.tls.fp then table.insert(p, "fp=" .. urlencode(o.tls.fp)) end
|
||||
if o.tls.alpn then table.insert(p, "alpn=" .. urlencode(o.tls.alpn)) end
|
||||
if o.tls.pcs then table.insert(p, "pcs=" .. urlencode(o.tls.pcs)) end
|
||||
table.insert(p, "allowInsecure=" .. (o.tls.insecure and "1" or "0"))
|
||||
|
||||
table.insert(p, "type=" .. o.transport.type)
|
||||
if o.transport.host then table.insert(p, "host=" .. urlencode(o.transport.host)) end
|
||||
if o.transport.path then table.insert(p, "path=" .. urlencode(o.transport.path)) end
|
||||
if o.transport.serviceName then table.insert(p, "serviceName=" .. urlencode(o.transport.serviceName)) end
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(o.name or "")
|
||||
end
|
||||
|
||||
-- VMess
|
||||
local function encode_vmess(node)
|
||||
local o = build_common(node)
|
||||
|
||||
local obj = {
|
||||
v = "2",
|
||||
ps = o.name,
|
||||
add = node.server,
|
||||
port = tostring(node.port),
|
||||
id = node.uuid,
|
||||
aid = tostring(node.alterId or 0),
|
||||
net = o.transport.type,
|
||||
security = node.cipher,
|
||||
scy = node.cipher,
|
||||
type = "none",
|
||||
host = o.transport.host or "",
|
||||
path = o.transport.path or "",
|
||||
tls = o.tls.security == "tls" and "tls" or "",
|
||||
sni = o.tls.sni,
|
||||
alpn = o.tls.alpn,
|
||||
fp = o.tls.fp,
|
||||
pcs = o.tls.pcs,
|
||||
insecure = o.tls.insecure and "1" or "0",
|
||||
tfo = node.tfo and "1" or "0"
|
||||
}
|
||||
|
||||
if o.transport.type == "grpc" then
|
||||
obj.path = o.transport.serviceName or ""
|
||||
end
|
||||
|
||||
return "vmess://" .. base64(json.stringify(obj))
|
||||
end
|
||||
|
||||
-- SS
|
||||
local function encode_ss(node)
|
||||
local userinfo = node.cipher .. ":" .. node.password
|
||||
local base = userinfo .. "@" .. host_format(node.server) .. ":" .. node.port
|
||||
local link = "ss://" .. base64(base)
|
||||
|
||||
local p = {}
|
||||
|
||||
if node.udp then table.insert(p, "udp=1") end
|
||||
if node["udp-over-tcp"] then table.insert(p, "uot=1") end
|
||||
|
||||
if node.plugin then
|
||||
local plugin = node.plugin
|
||||
if node["plugin-opts"] then
|
||||
local opts = {}
|
||||
for k, v in pairs(node["plugin-opts"]) do
|
||||
table.insert(opts, k .. "=" .. v)
|
||||
end
|
||||
plugin = plugin .. ";" .. table.concat(opts, ";")
|
||||
end
|
||||
table.insert(p, "plugin=" .. urlencode(plugin))
|
||||
end
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(node.name or "")
|
||||
end
|
||||
|
||||
-- Hysteria
|
||||
local function encode_hysteria2(node)
|
||||
local link = "hysteria://" .. host_format(node.server) .. ":" .. (node.port or "")
|
||||
local p = {}
|
||||
|
||||
if node["auth-str"] then table.insert(p, "auth=" .. node["auth-str"]) end
|
||||
if node["ports"] then table.insert(p, "mport=" .. node["ports"]) end
|
||||
if node.obfs then table.insert(p, "obfsParam=" .. node.obfs) end
|
||||
if node.sni then table.insert(p, "sni=" .. node.sni) end
|
||||
if node.up then table.insert(p, "upmbps=" .. node.up) end
|
||||
if node.down then table.insert(p, "downmbps=" .. node.down) end
|
||||
if node["skip-cert-verify"] then table.insert(p, "insecure=1") end
|
||||
if node["fingerprint"] then table.insert(p, "pinSHA256=" .. urlencode(node["fingerprint"])) end
|
||||
|
||||
if node.alpn then
|
||||
table.insert(p, "alpn=" .. urlencode(build_alpn(node.alpn)))
|
||||
end
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(node.name or "")
|
||||
end
|
||||
|
||||
-- Hysteria2
|
||||
local function encode_hysteria2(node)
|
||||
local link = "hysteria2://" .. (node.password or "") .. "@" .. host_format(node.server) .. ":" .. (node.port or "")
|
||||
local p = {}
|
||||
|
||||
if node["ports"] then table.insert(p, "mport=" .. urlencode(node["ports"])) end
|
||||
if node.obfs then table.insert(p, "obfs=" .. node.obfs) end
|
||||
if node["obfs-password"] then table.insert(p, "obfs-password=" .. node["obfs-password"]) end
|
||||
if node.up then table.insert(p, "upmbps=" .. node.up) end
|
||||
if node.down then table.insert(p, "downmbps=" .. node.down) end
|
||||
|
||||
if node.sni then table.insert(p, "sni=" .. urlencode(node.sni)) end
|
||||
if node["skip-cert-verify"] then table.insert(p, "insecure=1") end
|
||||
if node["fingerprint"] then table.insert(p, "pinSHA256=" .. urlencode(node["fingerprint"])) end
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(node.name or "")
|
||||
end
|
||||
|
||||
-- TUIC
|
||||
local function encode_tuic(node)
|
||||
local link = "tuic://" .. node.uuid .. ":" .. node.password .. "@" .. host_format(node.server) .. ":" .. node.port
|
||||
local p = {}
|
||||
|
||||
if node["congestion-controller"] then
|
||||
table.insert(p, "congestion_control=" .. node["congestion-controller"])
|
||||
end
|
||||
|
||||
if node.alpn then
|
||||
table.insert(p, "alpn=" .. urlencode(build_alpn(node.alpn)))
|
||||
end
|
||||
|
||||
if node.sni then table.insert(p, "sni=" .. urlencode(node.sni)) end
|
||||
if node["disable-sni"] then table.insert(p, "disable_sni=1") end
|
||||
if node["skip-cert-verify"] then table.insert(p, "allowInsecure=1") end
|
||||
if node["udp-relay-mode"] then table.insert(p, "udp_relay_mode=" .. node["udp-relay-mode"]) end
|
||||
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(node.name or "")
|
||||
end
|
||||
|
||||
-- AnyTLS
|
||||
local function encode_anytls(node)
|
||||
local link = "anytls://" .. (node.password or "") .. "@" .. host_format(node.server) .. ":" .. node.port
|
||||
local p = {}
|
||||
|
||||
if node.sni then table.insert(p, "sni=" .. urlencode(node.sni)) end
|
||||
if node["skip-cert-verify"] then table.insert(p, "allowInsecure=1") end
|
||||
|
||||
if node.alpn then
|
||||
table.insert(p, "alpn=" .. urlencode(build_alpn(node.alpn)))
|
||||
end
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return link .. "#" .. urlencode(node.name or "")
|
||||
end
|
||||
|
||||
-- SSR
|
||||
local function encode_ssr(node)
|
||||
local link = host_format(node.server) .. ":" .. node.port .. ":" .. (node.protocol or "") .. ":" ..
|
||||
(node.cipher or "") .. ":" .. (node.obfs or "") .. ":" .. base64(node.password)
|
||||
local p = {}
|
||||
|
||||
if node["obfs-param"] then table.insert(p, "obfsparam=" .. base64(node["obfs-param"])) end
|
||||
if node["protocol-param"] then table.insert(p, "protoparam=" .. base64(node["protocol-param"])) end
|
||||
table.insert(p, "remarks=" .. base64(node.name))
|
||||
|
||||
if #p > 0 then
|
||||
link = link .. "?" .. table.concat(p, "&")
|
||||
end
|
||||
|
||||
return "ssr://" .. base64(link)
|
||||
end
|
||||
|
||||
local function encode_node(node)
|
||||
if (not node.type) or (not node.name) then return nil end
|
||||
|
||||
local t = node.type
|
||||
|
||||
if t == "vless" then return encode_vless(node)
|
||||
elseif t == "trojan" then return encode_trojan(node)
|
||||
elseif t == "vmess" then return encode_vmess(node)
|
||||
elseif t == "ss" then return encode_ss(node)
|
||||
elseif t == "hysteria" then return encode_hysteria(node)
|
||||
elseif t == "hysteria2" then return encode_hysteria2(node)
|
||||
elseif t == "tuic" then return encode_tuic(node)
|
||||
elseif t == "anytls" then return encode_anytls(node)
|
||||
elseif t == "ssr" then return encode_ssr(node)
|
||||
else api.log("订阅转换 → 丢弃不支持的节点:" .. node.name .. ",节点类型:" .. t)
|
||||
end
|
||||
end
|
||||
|
||||
function parseClashNode(raw, remark)
|
||||
if not raw then return "" end
|
||||
local ok, lyaml = pcall(require, "lyaml")
|
||||
if not ok then return raw end
|
||||
|
||||
local data = lyaml.load(raw)
|
||||
if not data or type(data) ~= "table" then return raw end
|
||||
if not data.proxies then return "" end
|
||||
|
||||
api.log('检测到 Clash 订阅,正在进行转换 ...')
|
||||
|
||||
local links = {}
|
||||
for _, node in ipairs(data.proxies) do
|
||||
local link = encode_node(node)
|
||||
if link then
|
||||
table.insert(links, link)
|
||||
end
|
||||
end
|
||||
|
||||
return #links > 0 and table.concat(links, "\n") or ""
|
||||
end
|
||||
@@ -10,6 +10,8 @@ local appname = 'passwall'
|
||||
local api = require ("luci.passwall.api")
|
||||
local datatypes = require "luci.cbi.datatypes"
|
||||
|
||||
loadfile("/usr/share/" .. appname .. "/clash_subconverter.lua")()
|
||||
|
||||
-- these global functions are accessed all the time by the event handler
|
||||
-- so caching them is worth the effort
|
||||
local tinsert = table.insert
|
||||
@@ -1490,7 +1492,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
||||
end
|
||||
result.tls_serverName = params.sni
|
||||
result.tls_disable_sni = params.disable_sni
|
||||
result.tuic_alpn = params.alpn or "default"
|
||||
result.tuic_alpn = params.alpn or "h3"
|
||||
result.tuic_congestion_control = params.congestion_control or "cubic"
|
||||
result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
|
||||
local insecure = params.allowinsecure or params.insecure or params.allow_insecure
|
||||
@@ -1641,11 +1643,14 @@ local function curl(url, file, ua, mode)
|
||||
local curl_args = {
|
||||
"-fskL", "-w %{http_code}", "--retry 3", "--connect-timeout 3", "-H 'Accept-Encoding: identity'"
|
||||
}
|
||||
if ua and ua ~= "" and ua ~= "curl" then
|
||||
ua = (ua == "passwall") and ("passwall/" .. api.get_version()) or ua
|
||||
curl_args[#curl_args + 1] = '--user-agent "' .. ua .. '"'
|
||||
|
||||
ua = (ua and ua ~= "") and ua or "passwall"
|
||||
ua = (ua == "passwall") and ("passwall/" .. api.get_version()) or ua
|
||||
curl_args[#curl_args + 1] = '--user-agent "' .. ua .. '"'
|
||||
if not ua:lower():find("clash", 1, true) then
|
||||
curl_args[#curl_args + 1] = get_headers()
|
||||
end
|
||||
curl_args[#curl_args + 1] = get_headers()
|
||||
|
||||
local return_code, result
|
||||
if mode == "direct" then
|
||||
return_code, result = api.curl_base(url, file, curl_args)
|
||||
@@ -2126,6 +2131,7 @@ local execute = function()
|
||||
local cfgid = value[".name"]
|
||||
local remark = value.remark or ""
|
||||
local url = value.url or ""
|
||||
local tmp_file, ua
|
||||
|
||||
local url_is_local
|
||||
if fs.access(url) then
|
||||
@@ -2134,7 +2140,7 @@ local execute = function()
|
||||
url_is_local = true
|
||||
tmp_file = url
|
||||
else
|
||||
local ua = value.user_agent
|
||||
ua = value.user_agent
|
||||
local access_mode = value.access_mode
|
||||
local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连" or (access_mode == "proxy" and "代理" or "自动"))
|
||||
log('正在订阅:【' .. remark .. '】' .. url .. ' [' .. result .. ']')
|
||||
@@ -2157,6 +2163,7 @@ local execute = function()
|
||||
if not manual_sub and old_md5 == new_md5 then
|
||||
log('订阅:【' .. remark .. '】没有变化,无需更新。')
|
||||
else
|
||||
raw_data = parseClashNode(raw_data)
|
||||
parse_link(raw_data, "2", remark, value)
|
||||
uci:set(appname, cfgid, "md5", new_md5)
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user