mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-10 18:34:18 +08:00
⛅ Sync 2026-08-09 20:41:06
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gecoosac
|
||||
PKG_VERSION:=2.2.20251015
|
||||
PKG_RELEASE:=18
|
||||
PKG_RELEASE:=19
|
||||
|
||||
PKG_MAINTAINER:=Roc Lai <laipeng668@qq.com>
|
||||
PKG_LICENSE:=AGPL-3.0-only
|
||||
|
||||
@@ -86,6 +86,29 @@ normalize_path() {
|
||||
printf '%s\n' "$normalized"
|
||||
}
|
||||
|
||||
path_uses_clear_stage() {
|
||||
local path="$1" resolved
|
||||
|
||||
path="$(normalize_path "$path")" || return 1
|
||||
case "$path" in
|
||||
/.gecoosac-clear.*|/.gecoosac-clear.*/*|*/.gecoosac-clear.*|*/.gecoosac-clear.*/*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -e "$1" ] || [ -L "$1" ]; then
|
||||
resolved="$(readlink -f "$1" 2>/dev/null)" || return 1
|
||||
resolved="$(normalize_path "$resolved")" || return 1
|
||||
case "$resolved" in
|
||||
/.gecoosac-clear.*|/.gecoosac-clear.*/*|*/.gecoosac-clear.*|*/.gecoosac-clear.*/*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
is_safe_upload_dir() {
|
||||
local path physical
|
||||
|
||||
@@ -468,12 +491,20 @@ normalize_conf() {
|
||||
logger -t gecoosac "refusing unsupported upload directory: $upload_dir"
|
||||
return 1
|
||||
fi
|
||||
if path_uses_clear_stage "$upload_dir"; then
|
||||
logger -t gecoosac "refusing reserved upload cleanup stage path: $upload_dir"
|
||||
return 1
|
||||
fi
|
||||
if is_safe_db_dir "$db_dir" "$upload_dir"; then
|
||||
db_dir="$(normalize_path "$db_dir")"
|
||||
else
|
||||
logger -t gecoosac "refusing unsupported database directory: $db_dir"
|
||||
return 1
|
||||
fi
|
||||
if path_uses_clear_stage "$db_dir"; then
|
||||
logger -t gecoosac "refusing reserved upload cleanup stage path: $db_dir"
|
||||
return 1
|
||||
fi
|
||||
if [ "$isonlyoneprot" = "0" ] && [ "$https" = "1" ]; then
|
||||
is_abs_path "$crt_file" || {
|
||||
logger -t gecoosac "refusing non-absolute certificate path: $crt_file"
|
||||
@@ -483,6 +514,10 @@ normalize_conf() {
|
||||
logger -t gecoosac "refusing non-absolute key path: $key_file"
|
||||
return 1
|
||||
}
|
||||
if path_uses_clear_stage "$crt_file" || path_uses_clear_stage "$key_file"; then
|
||||
logger -t gecoosac "refusing reserved upload cleanup stage certificate path"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if is_safe_pid_dir "$piddir" "$upload_dir"; then
|
||||
piddir="$(normalize_path "$piddir")"
|
||||
@@ -490,6 +525,10 @@ normalize_conf() {
|
||||
logger -t gecoosac "refusing unsupported PID directory: $piddir"
|
||||
return 1
|
||||
fi
|
||||
if path_uses_clear_stage "$piddir"; then
|
||||
logger -t gecoosac "refusing reserved upload cleanup stage path: $piddir"
|
||||
return 1
|
||||
fi
|
||||
is_port "$port" || {
|
||||
logger -t gecoosac "refusing invalid interface port: $port"
|
||||
return 1
|
||||
@@ -659,8 +698,16 @@ start_prepared_service() {
|
||||
}
|
||||
|
||||
start_service() {
|
||||
prepare_service || exit 1
|
||||
start_prepared_service
|
||||
service_prepare_failed=0
|
||||
if ! prepare_service; then
|
||||
service_prepare_failed=1
|
||||
return 0
|
||||
fi
|
||||
start_prepared_service || service_prepare_failed=1
|
||||
}
|
||||
|
||||
service_started() {
|
||||
[ "${service_prepare_failed:-0}" = "0" ]
|
||||
}
|
||||
|
||||
service_running_state() {
|
||||
@@ -709,7 +756,7 @@ wait_service_stopped() {
|
||||
state="$?"
|
||||
case "$state" in
|
||||
0) return 0 ;;
|
||||
1)
|
||||
1|2)
|
||||
[ "$i" -ge 5 ] && return 1
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
|
||||
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-gecoosac
|
||||
PKG_VERSION:=2.2
|
||||
PKG_RELEASE:=18
|
||||
PKG_RELEASE:=19
|
||||
|
||||
LUCI_TITLE:=LuCI Support for gecoosac
|
||||
LUCI_DEPENDS:=+luci-base +gecoosac +gecoosac-files
|
||||
|
||||
@@ -14,6 +14,7 @@ const DEFAULT_PID_DIR = '/var/run';
|
||||
const CONFIG_BACKUP_DIR = '/etc/gecoosac';
|
||||
const DB_DIR_PREFIXES = [ '/etc/gecoosac', '/tmp/gecoosac', '/var/lib/gecoosac' ];
|
||||
const PID_DIR_PREFIXES = [ '/var/run', '/tmp/gecoosac' ];
|
||||
const CLEAR_STAGE_PATH_ERROR = _('Paths under .gecoosac-clear.* are reserved for upload cleanup.');
|
||||
|
||||
let statusPollRegistered = false;
|
||||
|
||||
@@ -92,7 +93,12 @@ function validateCertificatePath(section_id, value, singlePortOption, httpsOptio
|
||||
if (singlePortOption.formvalue(section_id) !== '0' || httpsOption.formvalue(section_id) !== '1' || !value)
|
||||
return true;
|
||||
|
||||
return String(value).charAt(0) === '/' ? true : _('Expecting an absolute path');
|
||||
if (String(value).charAt(0) !== '/')
|
||||
return _('Expecting an absolute path');
|
||||
|
||||
return usesClearStagePath(value)
|
||||
? CLEAR_STAGE_PATH_ERROR
|
||||
: true;
|
||||
}
|
||||
|
||||
function triggerActiveValidation(section_id, options) {
|
||||
@@ -134,6 +140,17 @@ function normalizePath(value) {
|
||||
return '/' + parts.join('/');
|
||||
}
|
||||
|
||||
function usesClearStagePath(value) {
|
||||
const path = normalizePath(value);
|
||||
const segments = path === null ? [] : path.split('/');
|
||||
|
||||
for (const segment of segments)
|
||||
if (segment.indexOf('.gecoosac-clear.') === 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function managedPath(value, policy) {
|
||||
const path = normalizePath(value);
|
||||
|
||||
@@ -247,14 +264,14 @@ function clientHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
function clientUrl() {
|
||||
const singlePort = uci.get('gecoosac', 'config', 'isonlyoneprot') !== '0';
|
||||
const https = uci.get('gecoosac', 'config', 'https') === '1';
|
||||
const port = singlePort
|
||||
? validPort(uci.get('gecoosac', 'config', 'port'), '60650')
|
||||
: validPort(uci.get('gecoosac', 'config', 'm_port'), '8080');
|
||||
function clientUrl(status) {
|
||||
const protocol = status && status.protocol;
|
||||
const port = validPort(status && status.port, null);
|
||||
|
||||
return (singlePort || !https ? 'http://' : 'https://') + clientHost() + ':' + port;
|
||||
if ((protocol === 'http' || protocol === 'https') && port !== null)
|
||||
return protocol + '://' + clientHost() + ':' + port;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function renderStatusContent(status) {
|
||||
@@ -263,12 +280,13 @@ function renderStatusContent(status) {
|
||||
(RPC_ERROR_MESSAGES[status.error] || _('Unable to query service status')));
|
||||
|
||||
const running = serviceRunning(status);
|
||||
const url = running ? clientUrl(status) : null;
|
||||
const text = running
|
||||
? _('The GecoosAC service is running.')
|
||||
: _('The GecoosAC service is not running.');
|
||||
const state = E('span', { 'class': running ? 'gecoosac-running' : 'gecoosac-stopped' }, text);
|
||||
|
||||
if (!running)
|
||||
if (!running || !url)
|
||||
return E('p', {}, state);
|
||||
|
||||
return E('p', {}, [
|
||||
@@ -276,7 +294,7 @@ function renderStatusContent(status) {
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-reload',
|
||||
'click': function() {
|
||||
const client = window.open(clientUrl(), '_blank', 'noopener');
|
||||
const client = window.open(url, '_blank', 'noopener');
|
||||
if (client)
|
||||
client.opener = null;
|
||||
}
|
||||
@@ -442,6 +460,8 @@ return view.extend({
|
||||
o.validate = function(section_id, value) {
|
||||
if (usesManagedPath(value) && !pathPolicy)
|
||||
return _('Unable to validate /var paths on this system.');
|
||||
if (usesClearStagePath(value))
|
||||
return CLEAR_STAGE_PATH_ERROR;
|
||||
|
||||
return validUploadDir(value, pathPolicy)
|
||||
? true
|
||||
@@ -458,6 +478,8 @@ return view.extend({
|
||||
const uploadDir = uploadDirOption.formvalue(section_id) || DEFAULT_UPLOAD_DIR;
|
||||
if ((usesManagedPath(value) || usesManagedPath(uploadDir)) && !pathPolicy)
|
||||
return _('Unable to validate /var paths on this system.');
|
||||
if (usesClearStagePath(value))
|
||||
return CLEAR_STAGE_PATH_ERROR;
|
||||
|
||||
if (!validPathPrefix(value, DB_DIR_PREFIXES))
|
||||
return _('Database directory must be under /etc/gecoosac, /tmp/gecoosac, or /var/lib/gecoosac.');
|
||||
@@ -477,6 +499,8 @@ return view.extend({
|
||||
const uploadDir = uploadDirOption.formvalue(section_id) || DEFAULT_UPLOAD_DIR;
|
||||
if ((usesManagedPath(value) || usesManagedPath(uploadDir)) && !pathPolicy)
|
||||
return _('Unable to validate /var paths on this system.');
|
||||
if (usesClearStagePath(value))
|
||||
return CLEAR_STAGE_PATH_ERROR;
|
||||
|
||||
if (!validPathPrefix(value, PID_DIR_PREFIXES))
|
||||
return _('PID directory must be under /var/run or /tmp/gecoosac.');
|
||||
|
||||
@@ -193,6 +193,9 @@ msgstr "无法验证上传目录清理暂存区"
|
||||
msgid "Upload cleanup stage contains a configured protected path"
|
||||
msgstr "上传目录清理暂存区包含已配置的受保护路径"
|
||||
|
||||
msgid "Paths under .gecoosac-clear.* are reserved for upload cleanup."
|
||||
msgstr ".gecoosac-clear.* 下的路径保留用于上传目录清理。"
|
||||
|
||||
msgid "Unable to recreate upload directory"
|
||||
msgstr "无法重新创建上传目录"
|
||||
|
||||
|
||||
@@ -193,6 +193,9 @@ msgstr "無法驗證上傳目錄清理暫存區"
|
||||
msgid "Upload cleanup stage contains a configured protected path"
|
||||
msgstr "上傳目錄清理暫存區包含已設定的受保護路徑"
|
||||
|
||||
msgid "Paths under .gecoosac-clear.* are reserved for upload cleanup."
|
||||
msgstr ".gecoosac-clear.* 下的路徑保留供上傳目錄清理使用。"
|
||||
|
||||
msgid "Unable to recreate upload directory"
|
||||
msgstr "無法重新建立上傳目錄"
|
||||
|
||||
|
||||
@@ -243,20 +243,78 @@ configured_path_in_upload() {
|
||||
}
|
||||
|
||||
status_result() {
|
||||
local ok="$1" running="$2" message="$3"
|
||||
local ok="$1" running="$2" message="$3" protocol="$4" port="$5"
|
||||
|
||||
json_init
|
||||
json_add_boolean ok "$ok"
|
||||
json_add_boolean running "$running"
|
||||
[ -n "$message" ] && json_add_string error "$message"
|
||||
[ -n "$protocol" ] && json_add_string protocol "$protocol"
|
||||
[ -n "$port" ] && json_add_string port "$port"
|
||||
json_dump
|
||||
json_cleanup
|
||||
}
|
||||
|
||||
runtime_endpoint() {
|
||||
local keys key argument pending interface_port management_port
|
||||
local single_port https_enabled
|
||||
|
||||
status_protocol=http
|
||||
status_port=
|
||||
pending=
|
||||
interface_port=
|
||||
management_port=
|
||||
single_port=1
|
||||
https_enabled=0
|
||||
|
||||
json_select command 2>/dev/null || return 1
|
||||
json_get_keys keys
|
||||
for key in $keys; do
|
||||
json_get_var argument "$key"
|
||||
if [ -n "$pending" ]; then
|
||||
case "$pending" in
|
||||
interface) interface_port="$argument" ;;
|
||||
management) management_port="$argument" ;;
|
||||
single) single_port="$argument" ;;
|
||||
https) https_enabled="$argument" ;;
|
||||
esac
|
||||
pending=
|
||||
continue
|
||||
fi
|
||||
|
||||
case "$argument" in
|
||||
-p) pending=interface ;;
|
||||
-mp) pending=management ;;
|
||||
-isonlyoneprot) pending=single ;;
|
||||
-https) pending=https ;;
|
||||
esac
|
||||
done
|
||||
json_select ..
|
||||
|
||||
case "$single_port" in
|
||||
0)
|
||||
[ -n "$management_port" ] || return 1
|
||||
status_port="$management_port"
|
||||
[ "$https_enabled" = "1" ] && status_protocol=https
|
||||
;;
|
||||
1)
|
||||
status_port="$interface_port"
|
||||
;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
|
||||
case "$status_port" in
|
||||
""|*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
[ "$status_port" -ge 1 ] 2>/dev/null && [ "$status_port" -le 65535 ]
|
||||
}
|
||||
|
||||
service_status() {
|
||||
local data instances instance running state
|
||||
local data instances instance running state status_protocol status_port
|
||||
|
||||
running=0
|
||||
status_protocol=
|
||||
status_port=
|
||||
data="$(ubus call service list '{"name":"gecoosac"}' 2>/dev/null)" || {
|
||||
status_result 0 0 "Unable to query service status"
|
||||
return
|
||||
@@ -272,17 +330,18 @@ service_status() {
|
||||
for instance in $instances; do
|
||||
json_select "$instance" 2>/dev/null || continue
|
||||
json_get_var state running
|
||||
json_select ..
|
||||
if [ "$state" = "1" ]; then
|
||||
running=1
|
||||
runtime_endpoint
|
||||
break
|
||||
fi
|
||||
json_select ..
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
json_cleanup
|
||||
status_result 1 "$running"
|
||||
status_result 1 "$running" "" "$status_protocol" "$status_port"
|
||||
}
|
||||
|
||||
path_policy() {
|
||||
|
||||
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall2
|
||||
PKG_VERSION:=26.8.7
|
||||
PKG_RELEASE:=73
|
||||
PKG_RELEASE:=74
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@@ -24,24 +24,16 @@ for k, e in ipairs(api.get_valid_nodes()) do
|
||||
end
|
||||
|
||||
local dynamicList_write = function(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
local new_t = {}
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
new_t = api.table_remove_duplicates(value)
|
||||
else
|
||||
t = { value }
|
||||
new_t = { value }
|
||||
end
|
||||
t = table.concat(t, " ")
|
||||
return DynamicList.write(self, section, t)
|
||||
local new_val = table.concat(new_t, " ")
|
||||
return DynamicList.write(self, section, new_val)
|
||||
end
|
||||
|
||||
local doh_validate = function(self, value, t)
|
||||
if value ~= "" then
|
||||
local flag = 0
|
||||
|
||||
@@ -168,23 +168,14 @@ source.validate = function(self, value, t)
|
||||
end
|
||||
|
||||
local dynamicList_write = function(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
local new_t = {}
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
new_t = api.table_remove_duplicates(value)
|
||||
else
|
||||
t = { value }
|
||||
new_t = { value }
|
||||
end
|
||||
t = table.concat(t, " ")
|
||||
return DynamicList.write(self, section, t)
|
||||
local new_val = table.concat(new_t, " ")
|
||||
return DynamicList.write(self, section, new_val)
|
||||
end
|
||||
|
||||
source.write = dynamicList_write
|
||||
|
||||
@@ -534,22 +534,13 @@ o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
|
||||
o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
|
||||
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
|
||||
function o.custom_write(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
local new_t
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
new_t = api.table_remove_duplicates(value)
|
||||
else
|
||||
t = { value }
|
||||
new_t = { value }
|
||||
end
|
||||
m:set(section, self.option:sub(1 + #option_prefix), t)
|
||||
m:set(section, self.option:sub(1 + #option_prefix), new_t)
|
||||
end
|
||||
|
||||
o = s:option(TextValue, _n("reality_mldsa65Verify"), "ML-DSA-65 " .. translate("Public key"))
|
||||
|
||||
@@ -590,22 +590,13 @@ o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
|
||||
o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
|
||||
o:depends({ [_n("tls")] = true })
|
||||
function o.custom_write(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
local new_t
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
new_t = api.table_remove_duplicates(value)
|
||||
else
|
||||
t = { value }
|
||||
new_t = { value }
|
||||
end
|
||||
m:set(section, self.option:sub(1 + #option_prefix), t)
|
||||
m:set(section, self.option:sub(1 + #option_prefix), new_t)
|
||||
end
|
||||
|
||||
o = s:option(Flag, _n("ech"), translate("ECH"))
|
||||
|
||||
@@ -218,23 +218,14 @@ o:depends({ [_n("reality")] = true })
|
||||
|
||||
o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames"))
|
||||
o:depends({ [_n("reality")] = true })
|
||||
function o.write(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
function o.custom_write(self, section, value)
|
||||
local new_t = {}
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
new_t = api.table_remove_duplicates(value)
|
||||
else
|
||||
t = { value }
|
||||
new_t = { value }
|
||||
end
|
||||
return DynamicList.write(self, section, t)
|
||||
m:set(section, self.option:sub(1 + #option_prefix), new_t)
|
||||
end
|
||||
|
||||
o = s:option(ListValue, _n("alpn"), translate("alpn"))
|
||||
|
||||
@@ -1863,3 +1863,19 @@ function get_network_devices()
|
||||
table.sort(_devices, function(a, b) return a.sort < b.sort end)
|
||||
return _devices
|
||||
end
|
||||
|
||||
function table_remove_duplicates(t)
|
||||
if not t or #t == 0 then return nil end
|
||||
local t_lookup = {}
|
||||
local new_t = {}
|
||||
local x
|
||||
for _, x in ipairs(t) do
|
||||
if x and #x > 0 then
|
||||
if not t_lookup[x] then
|
||||
t_lookup[x] = x
|
||||
new_t[#new_t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
return new_t
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user