1658 lines
55 KiB
Ucode
1658 lines
55 KiB
Ucode
#!/usr/bin/env ucode
|
|
|
|
let common = require("core.common");
|
|
let uci_core = require("core.uci");
|
|
|
|
let as_string = common.as_string;
|
|
let object_or_empty = common.object_or_empty;
|
|
let bool_value_common = common.bool_value;
|
|
let remove_file = common.remove_file;
|
|
let list_option = common.list_option;
|
|
|
|
function bool_value(value, fallback) {
|
|
if (value == null || value == "")
|
|
return !!fallback;
|
|
return bool_value_common(value);
|
|
}
|
|
|
|
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
|
|
const ITEM_TYPES = [
|
|
"subscription_url",
|
|
"section_interface",
|
|
"urltest",
|
|
"priority_group",
|
|
"priority_level"
|
|
];
|
|
|
|
let item_sections = null;
|
|
|
|
function raw_option(section, key) {
|
|
return object_or_empty(section)[key];
|
|
}
|
|
|
|
function option(section, key, fallback) {
|
|
return common.option(section, key, fallback);
|
|
}
|
|
|
|
function bool_option(section, key, fallback) {
|
|
let val = raw_option(section, key);
|
|
if (val == null)
|
|
return !!fallback;
|
|
return bool_value(val);
|
|
}
|
|
|
|
function list_value(section, key) {
|
|
let value = raw_option(section, key);
|
|
if (value == null)
|
|
return [];
|
|
if (type(value) == "array")
|
|
return value;
|
|
|
|
value = as_string(value);
|
|
return value == "" ? [] : [ value ];
|
|
}
|
|
|
|
function whitespace_list_value(section, key) {
|
|
let value = raw_option(section, key);
|
|
if (type(value) == "array")
|
|
return value;
|
|
return common.list_option(section, key);
|
|
}
|
|
|
|
function section_name(section) {
|
|
return option(section, ".name", "");
|
|
}
|
|
|
|
function empty_item_sections() {
|
|
let result = {};
|
|
for (let type_name in ITEM_TYPES) {
|
|
result[type_name] = {
|
|
by_name: {},
|
|
list: []
|
|
};
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function add_item_section(index, type_name, section) {
|
|
section = object_or_empty(section);
|
|
let name = section_name(section);
|
|
if (name == "")
|
|
return;
|
|
|
|
if (index[type_name] == null)
|
|
index[type_name] = { by_name: {}, list: [] };
|
|
index[type_name].by_name[name] = section;
|
|
push(index[type_name].list, section);
|
|
}
|
|
|
|
function item_index_from_cursor(cursor, config_name) {
|
|
let index = empty_item_sections();
|
|
if (cursor == null)
|
|
return index;
|
|
|
|
config_name = as_string(config_name || CONFIG_NAME);
|
|
for (let type_name in ITEM_TYPES) {
|
|
try {
|
|
cursor.foreach(config_name, type_name, function(section) {
|
|
add_item_section(index, type_name, section);
|
|
});
|
|
}
|
|
catch (e) {
|
|
// An absent config or an unknown section type throws here. The
|
|
// index simply ends up without entries of that type, which is the
|
|
// same state as a config that declares none; uci reports the parse
|
|
// error itself to the system log.
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
function fixture_section_list(data, type_name) {
|
|
data = object_or_empty(data);
|
|
let value = data[type_name];
|
|
if (type(value) == "array")
|
|
return value;
|
|
if (type(value) == "object")
|
|
return [ value ];
|
|
|
|
let plural = data[type_name + "s"];
|
|
return type(plural) == "array" ? plural : [];
|
|
}
|
|
|
|
function item_index_from_data(data) {
|
|
let index = empty_item_sections();
|
|
for (let type_name in ITEM_TYPES)
|
|
for (let section in fixture_section_list(data, type_name))
|
|
add_item_section(index, type_name, section);
|
|
return index;
|
|
}
|
|
|
|
function item_index_from_uci() {
|
|
let index = empty_item_sections();
|
|
for (let type_name in ITEM_TYPES)
|
|
for (let section in uci_core.section_objects(CONFIG_NAME, type_name))
|
|
add_item_section(index, type_name, section);
|
|
return index;
|
|
}
|
|
|
|
function set_item_sections(index) {
|
|
item_sections = type(index) == "object" ? index : empty_item_sections();
|
|
}
|
|
|
|
function set_item_sections_from_cursor(cursor, config_name) {
|
|
set_item_sections(item_index_from_cursor(cursor, config_name));
|
|
}
|
|
|
|
function set_item_sections_from_data(data) {
|
|
set_item_sections(item_index_from_data(data));
|
|
}
|
|
|
|
function get_item_sections() {
|
|
if (item_sections == null)
|
|
item_sections = item_index_from_uci();
|
|
return item_sections;
|
|
}
|
|
|
|
function child_section(type_name, item_id) {
|
|
return object_or_empty(object_or_empty(object_or_empty(get_item_sections())[type_name]).by_name)[as_string(item_id)];
|
|
}
|
|
|
|
function owned_child_section(parent, type_name, item_id) {
|
|
let child = child_section(type_name, item_id);
|
|
if (type(child) != "object")
|
|
return null;
|
|
return option(child, "section", "") == section_name(parent) ? child : null;
|
|
}
|
|
|
|
function child_item_sort(left, right) {
|
|
let left_has_order = (raw_option(left, "order") != null && raw_option(left, "order") != "");
|
|
let right_has_order = (raw_option(right, "order") != null && raw_option(right, "order") != "");
|
|
if (left_has_order && right_has_order) {
|
|
let left_order = int(option(left, "order", "0"), 10);
|
|
let right_order = int(option(right, "order", "0"), 10);
|
|
if (left_order != right_order)
|
|
return left_order < right_order ? -1 : 1;
|
|
} else if (left_has_order) {
|
|
return -1;
|
|
} else if (right_has_order) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function child_items(parent, type_name) {
|
|
let result = [];
|
|
for (let child in object_or_empty(object_or_empty(get_item_sections())[type_name]).list || [])
|
|
if (option(child, "section", "") == section_name(parent))
|
|
push(result, child);
|
|
sort(result, child_item_sort);
|
|
return result;
|
|
}
|
|
|
|
function child_items_by_owner(type_name, owner_key, owner_value) {
|
|
owner_value = as_string(owner_value);
|
|
let result = [];
|
|
for (let child in object_or_empty(object_or_empty(get_item_sections())[type_name]).list || [])
|
|
if (option(child, owner_key, "") == owner_value)
|
|
push(result, child);
|
|
sort(result, child_item_sort);
|
|
return result;
|
|
}
|
|
|
|
function child_item_by_value(parent, type_name, value_key, value) {
|
|
value = as_string(value);
|
|
for (let child in child_items(parent, type_name))
|
|
if (option(child, value_key, "") == value)
|
|
return child;
|
|
return null;
|
|
}
|
|
|
|
function child_option(child, key, fallback) {
|
|
if (type(child) != "object")
|
|
return as_string(fallback);
|
|
return option(child, key, fallback);
|
|
}
|
|
|
|
function child_option_alias(child, keys, fallback) {
|
|
if (type(child) != "object")
|
|
return as_string(fallback);
|
|
for (let key in keys) {
|
|
let value = raw_option(child, key);
|
|
if (value != null)
|
|
return as_string(value);
|
|
}
|
|
return as_string(fallback);
|
|
}
|
|
|
|
function child_bool(child, key, fallback) {
|
|
if (type(child) != "object")
|
|
return !!fallback;
|
|
return bool_option(child, key, fallback);
|
|
}
|
|
|
|
function child_list(child, key, fallback) {
|
|
if (type(child) != "object")
|
|
return fallback == null ? [] : fallback;
|
|
let value = raw_option(child, key);
|
|
if (value == null)
|
|
value = fallback;
|
|
if (value == null)
|
|
return [];
|
|
if (type(value) == "array")
|
|
return value;
|
|
|
|
value = as_string(value);
|
|
return value == "" ? [] : [ value ];
|
|
}
|
|
|
|
function child_list_alias(child, keys, fallback) {
|
|
if (type(child) == "object") {
|
|
for (let key in keys) {
|
|
let value = raw_option(child, key);
|
|
if (value != null)
|
|
return child_list(child, key, []);
|
|
}
|
|
}
|
|
return fallback == null ? [] : fallback;
|
|
}
|
|
|
|
function child_values(parent, type_name, value_key, legacy_key) {
|
|
let items = child_items(parent, type_name);
|
|
if (length(items) > 0) {
|
|
let result = [];
|
|
for (let child in items) {
|
|
let value = option(child, value_key, "");
|
|
if (value != "")
|
|
push(result, value);
|
|
}
|
|
return result;
|
|
}
|
|
return legacy_key == "" ? [] : whitespace_list_value(parent, legacy_key);
|
|
}
|
|
|
|
function settings_map(section, key) {
|
|
let value = option(section, key, "");
|
|
if (value == "")
|
|
return {};
|
|
|
|
try {
|
|
value = json(value);
|
|
}
|
|
catch (e) {
|
|
return {};
|
|
}
|
|
|
|
return object_or_empty(value);
|
|
}
|
|
|
|
function item_settings(section, key, item) {
|
|
return object_or_empty(settings_map(section, key)[as_string(item)]);
|
|
}
|
|
|
|
function item_option(section, key, item, option_name, fallback) {
|
|
let value = item_settings(section, key, item)[option_name];
|
|
if (value == null)
|
|
return as_string(fallback);
|
|
return as_string(value);
|
|
}
|
|
|
|
function item_bool(section, key, item, option_name, fallback) {
|
|
return bool_value(item_settings(section, key, item)[option_name], fallback);
|
|
}
|
|
|
|
function item_list(section, key, item, option_name, fallback) {
|
|
let value = item_settings(section, key, item)[option_name];
|
|
if (value == null)
|
|
value = fallback;
|
|
if (value == null)
|
|
return [];
|
|
if (type(value) == "array")
|
|
return value;
|
|
|
|
value = as_string(value);
|
|
return value == "" ? [] : [ value ];
|
|
}
|
|
|
|
function is_legacy_connection_action(action) {
|
|
action = as_string(action);
|
|
return action == "proxy" || action == "outbound" || action == "vpn";
|
|
}
|
|
|
|
function is_connections_action(action) {
|
|
action = as_string(action);
|
|
return action == "connection" || is_legacy_connection_action(action);
|
|
}
|
|
|
|
function is_remote_proxy_action(action) {
|
|
action = as_string(action);
|
|
return is_connections_action(action) ||
|
|
action == "awg" || action == "warp" ||
|
|
action == "anytls" || action == "snell" || action == "mieru" || action == "sudoku" ||
|
|
action == "masque" || action == "openvpn" ||
|
|
action == "wdtt" || action == "olcrtc" || action == "fptn";
|
|
}
|
|
|
|
function is_wdtt_action(action) {
|
|
return as_string(action) == "wdtt";
|
|
}
|
|
|
|
function is_olcrtc_action(action) {
|
|
return as_string(action) == "olcrtc";
|
|
}
|
|
|
|
function is_fptn_action(action) {
|
|
return as_string(action) == "fptn";
|
|
}
|
|
|
|
function wdtt_sections() {
|
|
let result = [];
|
|
let sections = uci_core.section_objects(CONFIG_NAME, "section");
|
|
for (let section in sections) {
|
|
if (bool_option(section, "enabled", true) && option(section, "action", "") == "wdtt")
|
|
push(result, section);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function olcrtc_sections() {
|
|
let result = [];
|
|
let sections = uci_core.section_objects(CONFIG_NAME, "section");
|
|
for (let section in sections) {
|
|
if (bool_option(section, "enabled", true) && option(section, "action", "") == "olcrtc")
|
|
push(result, section);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function fptn_sections() {
|
|
let result = [];
|
|
let sections = uci_core.section_objects(CONFIG_NAME, "section");
|
|
for (let section in sections) {
|
|
if (bool_option(section, "enabled", true) && option(section, "action", "") == "fptn")
|
|
push(result, section);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function normalize_action(action) {
|
|
action = as_string(action);
|
|
return is_connections_action(action) ? "connection" : action;
|
|
}
|
|
|
|
function action(section) {
|
|
return normalize_action(option(section, "action", ""));
|
|
}
|
|
|
|
function connection_urls(section) {
|
|
let links = whitespace_list_value(section, "selector_proxy_links");
|
|
let text = option(section, "selector_proxy_links_text", "");
|
|
if (text != "") {
|
|
for (let line in split(text, /[\r\n]+/)) {
|
|
line = trim(line);
|
|
if (line != "" && match(line, /^[^#\s]/))
|
|
push(links, line);
|
|
}
|
|
}
|
|
return links;
|
|
}
|
|
|
|
function urltest_text_links(section) {
|
|
let text = option(section, "urltest_proxy_links_text", "");
|
|
if (text == "")
|
|
return [];
|
|
let links = [];
|
|
for (let line in split(text, /[\r\n]+/)) {
|
|
line = trim(line);
|
|
if (line != "" && match(line, /^[^#\s]/))
|
|
push(links, line);
|
|
}
|
|
return links;
|
|
}
|
|
|
|
function subscription_urls(section) {
|
|
return child_values(section, "subscription_url", "url", "subscription_urls");
|
|
}
|
|
|
|
function interfaces(section) {
|
|
let items = child_values(section, "section_interface", "name", "");
|
|
if (length(items) > 0)
|
|
return items;
|
|
|
|
let result = list_value(section, "interfaces");
|
|
if (length(result) == 0) {
|
|
let value = option(section, "interface", "");
|
|
if (value != "")
|
|
push(result, value);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function outbound_jsons(section) {
|
|
let result = list_value(section, "outbound_jsons");
|
|
if (length(result) == 0) {
|
|
let value = option(section, "outbound_json", "");
|
|
if (value != "")
|
|
push(result, value);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function urltests(section) {
|
|
let result = [];
|
|
for (let child in child_items(section, "urltest")) {
|
|
let id = section_name(child);
|
|
if (id != "")
|
|
push(result, id);
|
|
}
|
|
if (length(result) > 0)
|
|
return result;
|
|
|
|
return bool_option(section, "urltest_enabled", false) ? [ "urltest" ] : [];
|
|
}
|
|
|
|
function priority_groups(section) {
|
|
let result = [];
|
|
for (let child in child_items(section, "priority_group")) {
|
|
let id = section_name(child);
|
|
if (id != "")
|
|
push(result, id);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function dashboard_filter_mode(section) {
|
|
return option(section, "dashboard_filter_mode", "disabled") || "disabled";
|
|
}
|
|
|
|
function dashboard_detect_server_country(section) {
|
|
return option(section, "dashboard_detect_server_country", "flag_emoji") || "flag_emoji";
|
|
}
|
|
|
|
function dashboard_include_countries(section) {
|
|
return list_value(section, "dashboard_include_countries");
|
|
}
|
|
|
|
function dashboard_include_outbounds(section) {
|
|
return list_value(section, "dashboard_include_outbounds");
|
|
}
|
|
|
|
function dashboard_include_regex(section) {
|
|
return list_value(section, "dashboard_include_regex");
|
|
}
|
|
|
|
function dashboard_include_proxy_parameters(section) {
|
|
return bool_option(section, "dashboard_include_proxy_parameters", false);
|
|
}
|
|
|
|
function dashboard_include_protocols(section) {
|
|
return list_value(section, "dashboard_include_protocols");
|
|
}
|
|
|
|
function dashboard_include_transports(section) {
|
|
return list_value(section, "dashboard_include_transports");
|
|
}
|
|
|
|
function dashboard_include_securities(section) {
|
|
return list_value(section, "dashboard_include_securities");
|
|
}
|
|
|
|
function dashboard_include_groups(section) {
|
|
return list_value(section, "dashboard_include_groups");
|
|
}
|
|
|
|
function dashboard_exclude_countries(section) {
|
|
return list_value(section, "dashboard_exclude_countries");
|
|
}
|
|
|
|
function dashboard_exclude_outbounds(section) {
|
|
return list_value(section, "dashboard_exclude_outbounds");
|
|
}
|
|
|
|
function dashboard_exclude_regex(section) {
|
|
return list_value(section, "dashboard_exclude_regex");
|
|
}
|
|
|
|
function dashboard_exclude_proxy_parameters(section) {
|
|
return bool_option(section, "dashboard_exclude_proxy_parameters", false);
|
|
}
|
|
|
|
function dashboard_exclude_protocols(section) {
|
|
return list_value(section, "dashboard_exclude_protocols");
|
|
}
|
|
|
|
function dashboard_exclude_transports(section) {
|
|
return list_value(section, "dashboard_exclude_transports");
|
|
}
|
|
|
|
function dashboard_exclude_securities(section) {
|
|
return list_value(section, "dashboard_exclude_securities");
|
|
}
|
|
|
|
function dashboard_exclude_groups(section) {
|
|
return list_value(section, "dashboard_exclude_groups");
|
|
}
|
|
|
|
function priority_group_child(section, value) {
|
|
return owned_child_section(section, "priority_group", value);
|
|
}
|
|
|
|
function priority_group_settings(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child : {};
|
|
}
|
|
|
|
function priority_level_child(group_id, value) {
|
|
let child = child_section("priority_level", value);
|
|
if (type(child) != "object")
|
|
return null;
|
|
return option(child, "group", "") == as_string(group_id) ? child : null;
|
|
}
|
|
|
|
function priority_level_sort(left, right) {
|
|
let left_order = int(option(left, "order", "0"), 10);
|
|
let right_order = int(option(right, "order", "0"), 10);
|
|
if (left_order != right_order)
|
|
return left_order < right_order ? -1 : 1;
|
|
|
|
let left_name = section_name(left);
|
|
let right_name = section_name(right);
|
|
return left_name == right_name ? 0 : (left_name < right_name ? -1 : 1);
|
|
}
|
|
|
|
function priority_level_items(group_id) {
|
|
let result = child_items_by_owner("priority_level", "group", group_id);
|
|
sort(result, priority_level_sort);
|
|
return result;
|
|
}
|
|
|
|
function priority_levels(group_id) {
|
|
let result = [];
|
|
for (let child in priority_level_items(group_id)) {
|
|
let id = section_name(child);
|
|
if (id != "")
|
|
push(result, id);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function community_lists(section) {
|
|
return whitespace_list_value(section, "community_lists");
|
|
}
|
|
|
|
function rule_sets(section) {
|
|
return whitespace_list_value(section, "rule_set");
|
|
}
|
|
|
|
function rule_sets_with_subnets(section) {
|
|
return whitespace_list_value(section, "rule_set_with_subnets");
|
|
}
|
|
|
|
function list_option_value_from_array(values) {
|
|
return join(" ", values || []);
|
|
}
|
|
|
|
function community_lists_value(section) {
|
|
return list_option_value_from_array(community_lists(section));
|
|
}
|
|
|
|
function rule_sets_value(section) {
|
|
return list_option_value_from_array(rule_sets(section));
|
|
}
|
|
|
|
function combined_domain_condition_text(section) {
|
|
if (type(raw_option(section, "domain")) != "array") {
|
|
let value = option(section, "domain", "");
|
|
if (value != "")
|
|
return value;
|
|
}
|
|
|
|
return option(section, "domain_suffix_text", "");
|
|
}
|
|
|
|
function rule_config_or_null() {
|
|
try {
|
|
return require("config.rule");
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function rule_condition_csv(section, key, kind) {
|
|
let rule_config = rule_config_or_null();
|
|
if (!rule_config)
|
|
return "";
|
|
return rule_config.rule_condition_csv_value(
|
|
key,
|
|
kind,
|
|
option(section, key + "_text_mode", "0"),
|
|
option(section, "conditions_text_mode", "0"),
|
|
option(section, key + "_text", ""),
|
|
option(section, key, ""),
|
|
combined_domain_condition_text(section),
|
|
option(section, "domain_suffix", "")
|
|
);
|
|
}
|
|
|
|
function has_dns_matchers(section) {
|
|
return rule_condition_csv(section, "domain", "domains") != "" ||
|
|
rule_condition_csv(section, "domain_suffix", "domains") != "" ||
|
|
rule_condition_csv(section, "domain_keyword", "generic") != "" ||
|
|
rule_condition_csv(section, "domain_regex", "generic") != "" ||
|
|
length(community_lists(section)) > 0 ||
|
|
length(rule_sets(section)) > 0 ||
|
|
length(rule_sets_with_subnets(section)) > 0 ||
|
|
option(section, "domain_ip_lists", "") != "";
|
|
}
|
|
|
|
const DSCP_ALIASES = {
|
|
"cs0": 0, "cs1": 8, "cs2": 16, "cs3": 24, "cs4": 32, "cs5": 40, "cs6": 48, "cs7": 56,
|
|
"ef": 46,
|
|
"af11": 10, "af12": 12, "af13": 14,
|
|
"af21": 18, "af22": 20, "af23": 22,
|
|
"af31": 26, "af32": 28, "af33": 30,
|
|
"af41": 34, "af42": 36, "af43": 38
|
|
};
|
|
|
|
function parse_dscp_value(item) {
|
|
item = lc(trim(as_string(item)));
|
|
if (item == "") return null;
|
|
if (DSCP_ALIASES[item] != null)
|
|
return DSCP_ALIASES[item];
|
|
|
|
let num = null;
|
|
if (substr(item, 0, 2) == "0x")
|
|
num = int(item, 16);
|
|
else
|
|
num = int(item, 10);
|
|
|
|
if (num != null && num >= 0 && num <= 63)
|
|
return num;
|
|
return null;
|
|
}
|
|
|
|
function dscp_list(section) {
|
|
let raw = whitespace_list_value(section, "dscp");
|
|
let result = [];
|
|
for (let item in raw) {
|
|
let parsed = parse_dscp_value(item);
|
|
if (parsed != null)
|
|
push(result, parsed);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function dscp_value(section) {
|
|
return list_option_value_from_array(dscp_list(section));
|
|
}
|
|
|
|
function contains(arr, val) {
|
|
if (type(arr) != "array") return false;
|
|
for (let item in arr) {
|
|
if (item == val) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function routed_dns_enabled(section) {
|
|
return bool_option(section, "routed_dns_enabled", false);
|
|
}
|
|
|
|
function routed_dns_type(section) {
|
|
let t = option(section, "routed_dns_type", "udp");
|
|
if (!contains(["udp", "dot", "doh", "doq"], t))
|
|
t = "udp";
|
|
return t;
|
|
}
|
|
|
|
function routed_dns_servers(section) {
|
|
let servers = list_option(section, "routed_dns_server");
|
|
if (length(servers) == 0) {
|
|
let single = option(section, "routed_dns_server", "");
|
|
servers = [single];
|
|
}
|
|
return servers;
|
|
}
|
|
|
|
function geoip_country_list(section) {
|
|
let raw = whitespace_list_value(section, "geoip_country");
|
|
let result = [];
|
|
for (let item in raw) {
|
|
item = lc(trim(as_string(item)));
|
|
if (item == "" || item == "all") continue;
|
|
if (item == "non-ru") {
|
|
if (!contains(result, "ru")) push(result, "ru");
|
|
continue;
|
|
}
|
|
if (match(item, /^[a-z]{2}$/) != null && !contains(result, item))
|
|
push(result, item);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function geoip_country_mode(section) {
|
|
let raw = option(section, "geoip_country", "all");
|
|
let mode = lc(trim(option(section, "geoip_mode", "")));
|
|
if (mode == "exclude" || mode == "include")
|
|
return mode;
|
|
|
|
if (raw == "non-ru")
|
|
return "exclude";
|
|
|
|
return "include";
|
|
}
|
|
|
|
function geoip_country_value(section) {
|
|
return list_option_value_from_array(geoip_country_list(section));
|
|
}
|
|
|
|
function rule_sets_with_subnets_value(section) {
|
|
return list_option_value_from_array(rule_sets_with_subnets(section));
|
|
}
|
|
|
|
function has_connection_sources(section) {
|
|
return length(connection_urls(section)) > 0 ||
|
|
length(subscription_urls(section)) > 0 ||
|
|
length(interfaces(section)) > 0 ||
|
|
length(outbound_jsons(section)) > 0;
|
|
}
|
|
|
|
function subscription_url_settings(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
return child != null ? child : item_settings(section, "subscription_url_settings", value);
|
|
}
|
|
|
|
function interface_settings(section, value) {
|
|
let child = child_item_by_value(section, "section_interface", "name", value);
|
|
return child != null ? child : item_settings(section, "interface_settings", value);
|
|
}
|
|
|
|
function interface_domain_resolver_enabled(section, value) {
|
|
let child = child_item_by_value(section, "section_interface", "name", value);
|
|
if (child != null)
|
|
return child_bool(child, "domain_resolver_enabled", false);
|
|
return item_bool(section, "interface_settings", value, "domain_resolver_enabled",
|
|
bool_option(section, "domain_resolver_enabled", false));
|
|
}
|
|
|
|
function interface_domain_resolver_dns_type(section, value) {
|
|
let child = child_item_by_value(section, "section_interface", "name", value);
|
|
if (child != null)
|
|
return child_option(child, "domain_resolver_dns_type", "udp");
|
|
return item_option(section, "interface_settings", value, "domain_resolver_dns_type",
|
|
option(section, "domain_resolver_dns_type", "udp") || "udp");
|
|
}
|
|
|
|
function interface_domain_resolver_dns_server(section, value) {
|
|
let child = child_item_by_value(section, "section_interface", "name", value);
|
|
if (child != null)
|
|
return child_option(child, "domain_resolver_dns_server", "8.8.8.8");
|
|
return item_option(section, "interface_settings", value, "domain_resolver_dns_server",
|
|
option(section, "domain_resolver_dns_server", "8.8.8.8") || "8.8.8.8");
|
|
}
|
|
|
|
function urltest_child(section, value) {
|
|
let child = owned_child_section(section, "urltest", value);
|
|
if (child != null)
|
|
return child;
|
|
|
|
return child_item_by_value(section, "urltest", "id", value);
|
|
}
|
|
|
|
function urltest_settings(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child : item_settings(section, "urltest_settings", value);
|
|
}
|
|
|
|
function subscription_update_enabled(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "subscription_update_enabled", true);
|
|
return item_bool(section, "subscription_url_settings", value, "subscription_update_enabled",
|
|
bool_option(section, "subscription_update_enabled", true));
|
|
}
|
|
|
|
function subscription_update_interval(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_option(child, "subscription_update_interval", "1h");
|
|
return item_option(section, "subscription_url_settings", value, "subscription_update_interval",
|
|
option(section, "subscription_update_interval", "1h") || "1h");
|
|
}
|
|
|
|
function subscription_dashboard_metadata_enabled(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "show_dashboard_metadata", true);
|
|
return item_bool(section, "subscription_url_settings", value, "show_dashboard_metadata", true);
|
|
}
|
|
|
|
function subscription_auto_user_agent(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null) {
|
|
if (raw_option(child, "auto_user_agent") != null)
|
|
return child_bool(child, "auto_user_agent", true);
|
|
return child_option(child, "user_agent", "") == "";
|
|
}
|
|
|
|
let settings = item_settings(section, "subscription_url_settings", value);
|
|
if (raw_option(settings, "auto_user_agent") != null)
|
|
return item_bool(section, "subscription_url_settings", value, "auto_user_agent", true);
|
|
return item_option(section, "subscription_url_settings", value, "user_agent", "") == "";
|
|
}
|
|
|
|
function subscription_auto_hwid(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null) {
|
|
if (raw_option(child, "auto_hwid") != null)
|
|
return child_bool(child, "auto_hwid", true);
|
|
return child_option(child, "hwid", "") == "";
|
|
}
|
|
|
|
let settings = item_settings(section, "subscription_url_settings", value);
|
|
if (raw_option(settings, "auto_hwid") != null)
|
|
return item_bool(section, "subscription_url_settings", value, "auto_hwid", true);
|
|
return item_option(section, "subscription_url_settings", value, "hwid", "") == "";
|
|
}
|
|
|
|
function subscription_include_urltest_groups(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "include_urltest_groups", true);
|
|
return item_bool(section, "subscription_url_settings", value, "include_urltest_groups", true);
|
|
}
|
|
|
|
function subscription_hide_urltest_group_outbounds(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "hide_urltest_group_outbounds", true);
|
|
return item_bool(section, "subscription_url_settings", value, "hide_urltest_group_outbounds", true);
|
|
}
|
|
|
|
function subscription_hide_detour_outbounds(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "hide_detour_outbounds", true);
|
|
return item_bool(section, "subscription_url_settings", value, "hide_detour_outbounds", true);
|
|
}
|
|
|
|
function subscription_prefix_nodes(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "prefix_nodes", false);
|
|
return item_bool(section, "subscription_url_settings", value, "prefix_nodes", false);
|
|
}
|
|
|
|
function subscription_node_prefix(section, value) {
|
|
if (!subscription_prefix_nodes(section, value))
|
|
return "";
|
|
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
let prefix = child != null
|
|
? child_option(child, "node_prefix", "")
|
|
: item_option(section, "subscription_url_settings", value, "node_prefix", "");
|
|
return trim(as_string(prefix));
|
|
}
|
|
|
|
function subscription_user_agent(section, value) {
|
|
if (subscription_auto_user_agent(section, value))
|
|
return "";
|
|
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
let configured = child != null
|
|
? child_option(child, "user_agent", "")
|
|
: item_option(section, "subscription_url_settings", value, "user_agent", "");
|
|
return configured != "" ? configured : "sing-box";
|
|
}
|
|
|
|
function subscription_hwid(section, value) {
|
|
if (subscription_auto_hwid(section, value))
|
|
return "";
|
|
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_option(child, "hwid", "");
|
|
return item_option(section, "subscription_url_settings", value, "hwid", "");
|
|
}
|
|
|
|
function subscription_custom_device_headers(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_bool(child, "custom_device_headers", false);
|
|
return item_bool(section, "subscription_url_settings", value, "custom_device_headers", false);
|
|
}
|
|
|
|
function subscription_device_header(section, value, name) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return child_option(child, name, "");
|
|
return item_option(section, "subscription_url_settings", value, name, "");
|
|
}
|
|
|
|
function subscription_device_os(section, value) {
|
|
return subscription_device_header(section, value, "device_os");
|
|
}
|
|
|
|
function subscription_ver_os(section, value) {
|
|
return subscription_device_header(section, value, "ver_os");
|
|
}
|
|
|
|
function subscription_device_model(section, value) {
|
|
return subscription_device_header(section, value, "device_model");
|
|
}
|
|
|
|
function subscription_device_locale(section, value) {
|
|
return subscription_device_header(section, value, "device_locale");
|
|
}
|
|
|
|
function subscription_app_version(section, value) {
|
|
return subscription_device_header(section, value, "app_version");
|
|
}
|
|
|
|
function subscription_accept_language(section, value) {
|
|
return subscription_device_header(section, value, "accept_language");
|
|
}
|
|
|
|
function subscription_device_headers(section, value) {
|
|
return {
|
|
enabled: subscription_custom_device_headers(section, value),
|
|
device_os: subscription_device_os(section, value),
|
|
ver_os: subscription_ver_os(section, value),
|
|
device_model: subscription_device_model(section, value),
|
|
device_locale: subscription_device_locale(section, value),
|
|
app_version: subscription_app_version(section, value),
|
|
accept_language: subscription_accept_language(section, value)
|
|
};
|
|
}
|
|
|
|
function device_headers_signature(headers) {
|
|
headers = object_or_empty(headers);
|
|
if (!headers.enabled)
|
|
return "";
|
|
|
|
return sprintf("%J", {
|
|
enabled: "1",
|
|
device_os: as_string(headers.device_os),
|
|
ver_os: as_string(headers.ver_os),
|
|
device_model: as_string(headers.device_model),
|
|
device_locale: as_string(headers.device_locale),
|
|
app_version: as_string(headers.app_version),
|
|
accept_language: as_string(headers.accept_language)
|
|
});
|
|
}
|
|
|
|
function subscription_device_headers_signature(section, value) {
|
|
return device_headers_signature(subscription_device_headers(section, value));
|
|
}
|
|
|
|
function subscription_download_section(section, value) {
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null) {
|
|
if (!child_bool(child, "download_via_proxy_enabled", false))
|
|
return "";
|
|
return child_option(child, "download_via_proxy_section", "");
|
|
}
|
|
|
|
if (!item_bool(section, "subscription_url_settings", value, "download_via_proxy_enabled", false))
|
|
return "";
|
|
|
|
return item_option(section, "subscription_url_settings", value, "download_via_proxy_section", "");
|
|
}
|
|
|
|
function subscription_insecure(section, value) {
|
|
if (section != null && section.subscription_insecure != null)
|
|
return bool_option(section, "subscription_insecure", false) ? "1" : "0";
|
|
|
|
let child = child_item_by_value(section, "subscription_url", "url", value);
|
|
if (child != null)
|
|
return bool_option(child, "subscription_insecure", false) ? "1" : "0";
|
|
|
|
return item_bool(section, "subscription_url_settings", value, "subscription_insecure", false) ? "1" : "0";
|
|
}
|
|
|
|
function urltest_check_interval(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option_alias(child, [ "check_interval", "urltest_check_interval" ], "3m");
|
|
return item_option(section, "urltest_settings", value, "urltest_check_interval",
|
|
option(section, "urltest_check_interval", "3m") || "3m");
|
|
}
|
|
|
|
function urltest_tolerance(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option_alias(child, [ "tolerance", "urltest_tolerance" ], "50");
|
|
return item_option(section, "urltest_settings", value, "urltest_tolerance",
|
|
option(section, "urltest_tolerance", "50") || "50");
|
|
}
|
|
|
|
function urltest_testing_url(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option_alias(child, [ "testing_url", "urltest_testing_url" ], "https://www.gstatic.com/generate_204");
|
|
return item_option(section, "urltest_settings", value, "urltest_testing_url",
|
|
option(section, "urltest_testing_url", "https://www.gstatic.com/generate_204") || "https://www.gstatic.com/generate_204");
|
|
}
|
|
|
|
function urltest_display_name(section, value) {
|
|
let fallback = as_string(value);
|
|
if (fallback == "urltest" && length(child_items(section, "urltest")) == 0)
|
|
fallback = "Fastest";
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option_alias(child, [ "name", "display_name" ], fallback);
|
|
return item_option(section, "urltest_settings", value, "display_name", fallback);
|
|
}
|
|
|
|
function urltest_idle_timeout(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option(child, "idle_timeout", "");
|
|
return item_option(section, "urltest_settings", value, "idle_timeout", "");
|
|
}
|
|
|
|
function urltest_interrupt_exist_connections(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_bool(child, "interrupt_exist_connections", true);
|
|
return item_bool(section, "urltest_settings", value, "interrupt_exist_connections", true);
|
|
}
|
|
|
|
function urltest_filter_mode(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option_alias(child, [ "filter_mode", "urltest_filter_mode" ], "disabled");
|
|
return item_option(section, "urltest_settings", value, "urltest_filter_mode",
|
|
option(section, "urltest_filter_mode", "disabled") || "disabled");
|
|
}
|
|
|
|
function urltest_detect_server_country(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_option(child, "detect_server_country", "flag_emoji");
|
|
return item_option(section, "urltest_settings", value, "detect_server_country",
|
|
option(section, "detect_server_country", "flag_emoji") || "flag_emoji");
|
|
}
|
|
|
|
function urltest_pin_dashboard(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_bool(child, "pin_dashboard", true);
|
|
return item_bool(section, "urltest_settings", value, "pin_dashboard", true);
|
|
}
|
|
|
|
function urltest_include_countries(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_list_alias(child, [ "include_countries", "urltest_include_countries" ], []);
|
|
return item_list(section, "urltest_settings", value, "urltest_include_countries",
|
|
list_value(section, "urltest_include_countries"));
|
|
}
|
|
|
|
function urltest_include_outbounds(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_list_alias(child, [ "include_outbounds", "urltest_include_outbounds" ], []);
|
|
return item_list(section, "urltest_settings", value, "urltest_include_outbounds",
|
|
list_value(section, "urltest_include_outbounds"));
|
|
}
|
|
|
|
function urltest_include_regex(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_list_alias(child, [ "include_regex", "urltest_include_regex" ], []);
|
|
return item_list(section, "urltest_settings", value, "urltest_include_regex",
|
|
list_value(section, "urltest_include_regex"));
|
|
}
|
|
|
|
function urltest_include_proxy_parameters(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_bool(child, "include_proxy_parameters", false) : false;
|
|
}
|
|
|
|
function urltest_include_protocols(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_list(child, "include_protocols", []) : [];
|
|
}
|
|
|
|
function urltest_include_transports(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_list(child, "include_transports", []) : [];
|
|
}
|
|
|
|
function urltest_include_securities(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_list(child, "include_securities", []) : [];
|
|
}
|
|
|
|
function urltest_exclude_countries(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_list_alias(child, [ "exclude_countries", "urltest_exclude_countries" ], []);
|
|
return item_list(section, "urltest_settings", value, "urltest_exclude_countries",
|
|
list_value(section, "urltest_exclude_countries"));
|
|
}
|
|
|
|
function urltest_exclude_outbounds(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_list_alias(child, [ "exclude_outbounds", "urltest_exclude_outbounds" ], []);
|
|
return item_list(section, "urltest_settings", value, "urltest_exclude_outbounds",
|
|
list_value(section, "urltest_exclude_outbounds"));
|
|
}
|
|
|
|
function urltest_exclude_regex(section, value) {
|
|
let child = urltest_child(section, value);
|
|
if (child != null)
|
|
return child_list_alias(child, [ "exclude_regex", "urltest_exclude_regex" ], []);
|
|
return item_list(section, "urltest_settings", value, "urltest_exclude_regex",
|
|
list_value(section, "urltest_exclude_regex"));
|
|
}
|
|
|
|
function urltest_exclude_proxy_parameters(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_bool(child, "exclude_proxy_parameters", false) : false;
|
|
}
|
|
|
|
function urltest_exclude_protocols(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_list(child, "exclude_protocols", []) : [];
|
|
}
|
|
|
|
function urltest_exclude_transports(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_list(child, "exclude_transports", []) : [];
|
|
}
|
|
|
|
function urltest_exclude_securities(section, value) {
|
|
let child = urltest_child(section, value);
|
|
return child != null ? child_list(child, "exclude_securities", []) : [];
|
|
}
|
|
|
|
function priority_group_display_name(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_option(child, "name", as_string(value)) : as_string(value);
|
|
}
|
|
|
|
function priority_group_health_url(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_option(child, "health_url", "https://www.gstatic.com/generate_204") : "https://www.gstatic.com/generate_204";
|
|
}
|
|
|
|
function priority_group_active_check_interval(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_option(child, "active_check_interval", "5s") : "5s";
|
|
}
|
|
|
|
function priority_group_check_timeout(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_option(child, "check_timeout", "2s") : "2s";
|
|
}
|
|
|
|
function priority_group_recovery_check_interval(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_option(child, "recovery_check_interval", "15s") : "15s";
|
|
}
|
|
|
|
function priority_group_pick_fastest(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_bool(child, "pick_fastest", false) : false;
|
|
}
|
|
|
|
function priority_group_switch_to_faster_same_priority(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_bool(child, "switch_to_faster_same_priority", false) : false;
|
|
}
|
|
|
|
function priority_group_fastest_check_interval(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_option(child, "fastest_check_interval", "3m") : "3m";
|
|
}
|
|
|
|
function priority_group_interrupt_exist_connections(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_bool(child, "interrupt_exist_connections", true) : true;
|
|
}
|
|
|
|
function priority_group_pin_dashboard(section, value) {
|
|
let child = priority_group_child(section, value);
|
|
return child != null ? child_bool(child, "pin_dashboard", true) : true;
|
|
}
|
|
|
|
function priority_level_display_name(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_option(child, "name", as_string(value)) : as_string(value);
|
|
}
|
|
|
|
function priority_level_order(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_option(child, "order", "0") : "0";
|
|
}
|
|
|
|
function priority_level_direct(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_bool(child, "direct", false) : false;
|
|
}
|
|
|
|
function priority_level_filter_mode(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_option(child, "filter_mode", "include") : "include";
|
|
}
|
|
|
|
function priority_level_include_countries(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list_alias(child, [ "include_countries", "country" ], []) : [];
|
|
}
|
|
|
|
function priority_level_include_outbounds(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list_alias(child, [ "include_outbounds", "server_name" ], []) : [];
|
|
}
|
|
|
|
function priority_level_include_regex(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list_alias(child, [ "include_regex", "regex" ], []) : [];
|
|
}
|
|
|
|
function priority_level_include_proxy_parameters(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_bool(child, "include_proxy_parameters", false) : false;
|
|
}
|
|
|
|
function priority_level_include_protocols(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "include_protocols", []) : [];
|
|
}
|
|
|
|
function priority_level_include_transports(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "include_transports", []) : [];
|
|
}
|
|
|
|
function priority_level_include_securities(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "include_securities", []) : [];
|
|
}
|
|
|
|
function priority_level_exclude_countries(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "exclude_countries", []) : [];
|
|
}
|
|
|
|
function priority_level_exclude_outbounds(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "exclude_outbounds", []) : [];
|
|
}
|
|
|
|
function priority_level_exclude_regex(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "exclude_regex", []) : [];
|
|
}
|
|
|
|
function priority_level_exclude_proxy_parameters(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_bool(child, "exclude_proxy_parameters", false) : false;
|
|
}
|
|
|
|
function priority_level_exclude_protocols(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "exclude_protocols", []) : [];
|
|
}
|
|
|
|
function priority_level_exclude_transports(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "exclude_transports", []) : [];
|
|
}
|
|
|
|
function priority_level_exclude_securities(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_list(child, "exclude_securities", []) : [];
|
|
}
|
|
|
|
function priority_level_countries(group_id, value) {
|
|
return priority_level_include_countries(group_id, value);
|
|
}
|
|
|
|
function priority_level_outbounds(group_id, value) {
|
|
return priority_level_include_outbounds(group_id, value);
|
|
}
|
|
|
|
function priority_level_regex(group_id, value) {
|
|
return priority_level_include_regex(group_id, value);
|
|
}
|
|
|
|
function priority_level_detect_server_country(group_id, value) {
|
|
let child = priority_level_child(group_id, value);
|
|
return child != null ? child_option(child, "detect_server_country", "flag_emoji") : "flag_emoji";
|
|
}
|
|
|
|
function append_unique(result, seen, value) {
|
|
value = as_string(value);
|
|
if (value == "" || seen[value])
|
|
return;
|
|
seen[value] = true;
|
|
push(result, value);
|
|
}
|
|
|
|
function subscription_download_targets(sections) {
|
|
let result = [];
|
|
let seen = {};
|
|
|
|
for (let section in sections) {
|
|
section = object_or_empty(section);
|
|
let name = option(section, ".name", "");
|
|
|
|
for (let source in subscription_urls(section)) {
|
|
let target = subscription_download_section(section, source);
|
|
if (target != "")
|
|
append_unique(result, seen, target);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function subscription_download_target_port(sections, target, base_port) {
|
|
target = as_string(target);
|
|
base_port = int(base_port || 0);
|
|
|
|
let targets = subscription_download_targets(sections);
|
|
for (let i = 0; i < length(targets); i++)
|
|
if (targets[i] == target)
|
|
return base_port + 2 + i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Cascade-delete all child sections (subscription_url, urltest, etc.) that
|
|
// belong to the given parent section, along with cross-references and disk caches.
|
|
// Returns the number of children removed.
|
|
function cascade_delete_section(cursor, config_name, parent_name) {
|
|
let removed = 0;
|
|
config_name = as_string(config_name || CONFIG_NAME);
|
|
parent_name = as_string(parent_name || "");
|
|
if (parent_name == "")
|
|
return 0;
|
|
|
|
// 1. Delete priority_level belonging to priority_group in this section
|
|
try {
|
|
let group_names = [];
|
|
cursor.foreach(config_name, "priority_group", function(section) {
|
|
if (option(section, "section", "") == parent_name)
|
|
push(group_names, section_name(section));
|
|
});
|
|
for (let gname in group_names) {
|
|
let level_names = [];
|
|
cursor.foreach(config_name, "priority_level", function(section) {
|
|
if (option(section, "group", "") == gname || option(section, "section", "") == parent_name)
|
|
push(level_names, section_name(section));
|
|
});
|
|
for (let lname in level_names) {
|
|
cursor.delete(config_name, lname);
|
|
removed++;
|
|
}
|
|
cursor.delete(config_name, gname);
|
|
removed++;
|
|
}
|
|
} catch (e) {}
|
|
|
|
// 2. Delete all other child sections (subscription_url, section_interface, urltest, priority_level)
|
|
for (let type_name in [ "subscription_url", "section_interface", "urltest", "priority_level" ]) {
|
|
try {
|
|
let to_delete = [];
|
|
cursor.foreach(config_name, type_name, function(section) {
|
|
if (option(section, "section", "") == parent_name)
|
|
push(to_delete, section_name(section));
|
|
});
|
|
for (let name in to_delete) {
|
|
cursor.delete(config_name, name);
|
|
removed++;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
// 3. Clean references in settings
|
|
try {
|
|
let smart_detect = cursor.get(config_name, "settings", "smart_detect_sections");
|
|
if (type(smart_detect) == "array") {
|
|
let filtered = [];
|
|
for (let sec in smart_detect) {
|
|
if (as_string(sec) != parent_name)
|
|
push(filtered, sec);
|
|
}
|
|
if (length(filtered) != length(smart_detect))
|
|
cursor.set(config_name, "settings", "smart_detect_sections", filtered);
|
|
} else if (as_string(smart_detect) == parent_name) {
|
|
cursor.delete(config_name, "settings", "smart_detect_sections");
|
|
}
|
|
|
|
for (let opt in [ "dns_detour_section", "download_lists_via_proxy_section", "download_components_via_proxy_section", "warp_proxy_section" ]) {
|
|
if (as_string(cursor.get(config_name, "settings", opt)) == parent_name)
|
|
cursor.delete(config_name, "settings", opt);
|
|
}
|
|
} catch (e) {}
|
|
|
|
// 4. Clean references in other sections
|
|
try {
|
|
cursor.foreach(config_name, "section", function(sec) {
|
|
let sname = section_name(sec);
|
|
if (sname == parent_name) return;
|
|
if (option(sec, "outbound_detour_section", "") == parent_name) {
|
|
cursor.delete(config_name, sname, "outbound_detour_section");
|
|
cursor.set(config_name, sname, "outbound_detour_enabled", "0");
|
|
}
|
|
if (option(sec, "dns_detour_section", "") == parent_name) {
|
|
cursor.delete(config_name, sname, "dns_detour_section");
|
|
cursor.set(config_name, sname, "dns_detour_enabled", "0");
|
|
}
|
|
});
|
|
} catch (e) {}
|
|
|
|
// 5. Clean references in servers
|
|
try {
|
|
cursor.foreach(config_name, "server", function(srv) {
|
|
let sname = section_name(srv);
|
|
if (option(srv, "routing_section", "") == parent_name) {
|
|
cursor.delete(config_name, sname, "routing_section");
|
|
if (option(srv, "routing_mode", "") == "section")
|
|
cursor.set(config_name, sname, "routing_mode", "rules");
|
|
}
|
|
});
|
|
} catch (e) {}
|
|
|
|
// 6. Delete section cache files
|
|
try {
|
|
remove_file("/var/run/tachyon/section-cache/" + parent_name + ".json");
|
|
remove_file("/etc/tachyon/subscription-cache/" + parent_name + ".json");
|
|
remove_file("/etc/tachyon/subscription-cache/" + parent_name + ".yaml");
|
|
remove_file("/etc/tachyon/subscription-cache/" + parent_name + ".txt");
|
|
} catch (e) {}
|
|
|
|
return removed;
|
|
}
|
|
|
|
function cli_delete_section(sec_name) {
|
|
sec_name = trim(as_string(sec_name));
|
|
if (sec_name == "") {
|
|
warn("Usage: tachyon delete_section <section_name>\n");
|
|
return 1;
|
|
}
|
|
let c = uci_core.cursor();
|
|
c.load(CONFIG_NAME);
|
|
let s = c.get_all(CONFIG_NAME, sec_name);
|
|
if (!s) {
|
|
warn("Section '" + sec_name + "' not found\n");
|
|
return 1;
|
|
}
|
|
cascade_delete_section(c, CONFIG_NAME, sec_name);
|
|
c.delete(CONFIG_NAME, sec_name);
|
|
c.commit(CONFIG_NAME);
|
|
print(sprintf("{\"success\":true,\"deleted\":%J}\n", sec_name));
|
|
return 0;
|
|
}
|
|
|
|
let cli_mode = as_string(ARGV[0]);
|
|
if (cli_mode == "delete-section")
|
|
exit(cli_delete_section(ARGV[1]));
|
|
|
|
return {
|
|
option,
|
|
bool_option,
|
|
list_value,
|
|
whitespace_list_value,
|
|
cascade_delete_section,
|
|
set_item_sections,
|
|
set_item_sections_from_cursor,
|
|
set_item_sections_from_data,
|
|
settings_map,
|
|
item_settings,
|
|
item_option,
|
|
item_bool,
|
|
is_connections_action,
|
|
is_remote_proxy_action,
|
|
normalize_action,
|
|
action,
|
|
connection_urls,
|
|
subscription_urls,
|
|
interfaces,
|
|
outbound_jsons,
|
|
urltests,
|
|
dashboard_filter_mode,
|
|
dashboard_detect_server_country,
|
|
dashboard_include_countries,
|
|
dashboard_include_outbounds,
|
|
dashboard_include_regex,
|
|
dashboard_include_proxy_parameters,
|
|
dashboard_include_protocols,
|
|
dashboard_include_transports,
|
|
dashboard_include_securities,
|
|
dashboard_include_groups,
|
|
dashboard_exclude_countries,
|
|
dashboard_exclude_outbounds,
|
|
dashboard_exclude_regex,
|
|
dashboard_exclude_proxy_parameters,
|
|
dashboard_exclude_protocols,
|
|
dashboard_exclude_transports,
|
|
dashboard_exclude_securities,
|
|
dashboard_exclude_groups,
|
|
community_lists,
|
|
rule_sets,
|
|
rule_sets_with_subnets,
|
|
dscp_list,
|
|
dscp_value,
|
|
community_lists_value,
|
|
rule_sets_value,
|
|
rule_sets_with_subnets_value,
|
|
has_connection_sources,
|
|
subscription_url_settings,
|
|
interface_settings,
|
|
urltest_settings,
|
|
subscription_update_enabled,
|
|
subscription_update_interval,
|
|
subscription_dashboard_metadata_enabled,
|
|
subscription_auto_user_agent,
|
|
subscription_auto_hwid,
|
|
subscription_include_urltest_groups,
|
|
subscription_hide_urltest_group_outbounds,
|
|
subscription_hide_detour_outbounds,
|
|
subscription_prefix_nodes,
|
|
subscription_node_prefix,
|
|
subscription_user_agent,
|
|
subscription_hwid,
|
|
subscription_custom_device_headers,
|
|
subscription_device_os,
|
|
subscription_ver_os,
|
|
subscription_device_model,
|
|
subscription_device_locale,
|
|
subscription_app_version,
|
|
subscription_accept_language,
|
|
subscription_device_headers,
|
|
device_headers_signature,
|
|
subscription_device_headers_signature,
|
|
subscription_download_section,
|
|
subscription_insecure,
|
|
urltest_text_links,
|
|
interface_domain_resolver_enabled,
|
|
interface_domain_resolver_dns_type,
|
|
interface_domain_resolver_dns_server,
|
|
urltest_check_interval,
|
|
urltest_tolerance,
|
|
urltest_testing_url,
|
|
urltest_display_name,
|
|
urltest_idle_timeout,
|
|
urltest_interrupt_exist_connections,
|
|
urltest_filter_mode,
|
|
urltest_detect_server_country,
|
|
urltest_pin_dashboard,
|
|
urltest_include_countries,
|
|
urltest_include_outbounds,
|
|
urltest_include_regex,
|
|
urltest_include_proxy_parameters,
|
|
urltest_include_protocols,
|
|
urltest_include_transports,
|
|
urltest_include_securities,
|
|
urltest_exclude_countries,
|
|
urltest_exclude_outbounds,
|
|
urltest_exclude_regex,
|
|
urltest_exclude_proxy_parameters,
|
|
urltest_exclude_protocols,
|
|
urltest_exclude_transports,
|
|
urltest_exclude_securities,
|
|
priority_groups,
|
|
priority_group_settings,
|
|
priority_levels,
|
|
priority_group_display_name,
|
|
priority_group_health_url,
|
|
priority_group_active_check_interval,
|
|
priority_group_check_timeout,
|
|
priority_group_recovery_check_interval,
|
|
priority_group_pick_fastest,
|
|
priority_group_switch_to_faster_same_priority,
|
|
priority_group_fastest_check_interval,
|
|
priority_group_interrupt_exist_connections,
|
|
priority_group_pin_dashboard,
|
|
priority_level_display_name,
|
|
priority_level_order,
|
|
priority_level_direct,
|
|
priority_level_filter_mode,
|
|
priority_level_include_countries,
|
|
priority_level_include_outbounds,
|
|
priority_level_include_regex,
|
|
priority_level_include_proxy_parameters,
|
|
priority_level_include_protocols,
|
|
priority_level_include_transports,
|
|
priority_level_include_securities,
|
|
priority_level_exclude_countries,
|
|
priority_level_exclude_outbounds,
|
|
priority_level_exclude_regex,
|
|
priority_level_exclude_proxy_parameters,
|
|
priority_level_exclude_protocols,
|
|
priority_level_exclude_transports,
|
|
priority_level_exclude_securities,
|
|
priority_level_countries,
|
|
priority_level_outbounds,
|
|
priority_level_regex,
|
|
priority_level_detect_server_country,
|
|
parse_dscp_value,
|
|
dscp_list,
|
|
dscp_value,
|
|
geoip_country_list,
|
|
geoip_country_mode,
|
|
geoip_country_value,
|
|
subscription_download_targets,
|
|
subscription_download_target_port,
|
|
has_dns_matchers,
|
|
routed_dns_enabled,
|
|
routed_dns_type,
|
|
routed_dns_servers,
|
|
item_index_from_cursor,
|
|
cli_delete_section,
|
|
wdtt_sections,
|
|
olcrtc_sections,
|
|
fptn_sections,
|
|
is_wdtt_action,
|
|
is_olcrtc_action,
|
|
is_fptn_action
|
|
};
|