Explorar el Código

luci: add new smartdns luci lite version

Nick Peng hace 1 año
padre
commit
fca78d7772

+ 1 - 1
package/luci-compat/control/postinst

@@ -16,6 +16,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 [ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0
-[ -x ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
+[ -e ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
 . ${IPKG_INSTROOT}/lib/functions.sh
 default_postinst $0 $@

+ 0 - 4
package/luci-compat/control/postinst-pkg

@@ -1,4 +0,0 @@
-[ -n "${IPKG_INSTROOT}" ] || {
-	(. /etc/uci-defaults/50_luci-smartdns) && rm -f /etc/uci-defaults/50_luci-smartdns
-	exit 0
-}

+ 1 - 1
package/luci-compat/control/prerm

@@ -15,6 +15,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-[ -x ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
+[ -e ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
 . ${IPKG_INSTROOT}/lib/functions.sh
 default_prerm $0 $@

+ 1 - 0
package/luci-compat/make.sh

@@ -50,6 +50,7 @@ build()
 
 	mkdir -p $ROOT
 	cp $CURR_DIR/* $ROOT/ -af
+	cp $CURR_DIR/../tool $ROOT/ -af
 	cd $ROOT/
 	build_tool
 	mkdir $ROOT/root/usr/lib/lua/ -p

+ 1 - 0
package/luci-lite/control/conffiles

@@ -0,0 +1 @@
+/etc/config/smartdns-lite

+ 7 - 0
package/luci-lite/control/control

@@ -0,0 +1,7 @@
+Package: luci-app-smartdns-lite
+Version: git-18.201.27126-7bf0367-1
+Depends: libc, smartdns
+Source: feeds/luci/applications/luci-app-smartdns-lite
+Section: luci
+Architecture: all
+Description:  A smartdns server for china

+ 24 - 0
package/luci-lite/control/postinst

@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+#
+# smartdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# smartdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+[ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0
+[ -e ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
+. ${IPKG_INSTROOT}/lib/functions.sh
+default_postinst $0 $@
+ret=$?
+/etc/init.d/smartdns-lite enable
+exit 0

+ 23 - 0
package/luci-lite/control/prerm

@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+#
+# smartdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# smartdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+[ -e ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
+. ${IPKG_INSTROOT}/lib/functions.sh
+default_prerm $0 $@
+/etc/init.d/smartdns-lite disable
+rm /var/etc/smartdns-lite.conf -f
+exit 0

+ 1 - 0
package/luci-lite/debian-binary

@@ -0,0 +1 @@
+2.0

+ 211 - 0
package/luci-lite/files/luci/i18n/smartdns-lite.zh-cn.po

@@ -0,0 +1,211 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "A local SmartDNS server for lite users."
+msgstr "为入门用户提供的本地SmartDNS服务器。"
+
+msgid "AD Block Domain List File"
+msgstr "广告屏蔽域名列表文件"
+
+msgid "Basic Settings"
+msgstr "基础设置"
+
+msgid "Block Domain List File for Parental Control."
+msgstr "家长控制功能屏蔽的域名列表文件。"
+
+msgid "Client Address"
+msgstr "客户端地址"
+
+msgid "Client Address File"
+msgstr "客户端地址文件"
+
+msgid "Client address format error, please input ip adress or mac address."
+msgstr "客户端地址格式错误,请输入IP地址或MAC地址。"
+
+msgid "CloudFlare CDN IP File"
+msgstr "CloudFlare CDN IP文件"
+
+msgid "CloudFlare CDN IP Settings"
+msgstr "CloudFlare CDN IP设置"
+
+msgid "Collecting data ..."
+msgstr "正在收集数据..."
+
+msgid "Custom Settings"
+msgstr "自定义设置"
+
+msgid "DNS Server Mode"
+msgstr "DNS服务器模式"
+
+msgid "DNS Server Port"
+msgstr "DNS服务器端口号"
+
+msgid "Dnsmasq Forwarded To Smartdns Failure"
+msgstr "设置smartdns为dnsmasq上游失败"
+
+msgid "Domain List File"
+msgstr "域名列表文件"
+
+msgid "Domain Rules Settings"
+msgstr "域名规则设置"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Enable or disable cloudflare cdn ip accelerating."
+msgstr "启用或禁用cloudflare CDN IP加速。"
+
+msgid "Enable or disable domain rules."
+msgstr "启用或禁用域名规则。"
+
+msgid "Enable or disable smartdns server"
+msgstr "启用或禁用smartdns服务器"
+
+msgid "Force AAAA SOA"
+msgstr "禁止AAAA记录"
+
+msgid "Force AAAA SOA."
+msgstr "强制IPV6记录返回SOA。"
+
+msgid "Force HTTPS SOA"
+msgstr "禁用HTTPS记录"
+
+msgid "Force HTTPS SOA."
+msgstr "强制HTTPS记录返回SOA"
+
+msgid "Grant access to LuCI app smartdns"
+msgstr "获取访问smartdns的权限"
+
+msgid "IP Address Mapping, mapping all CloudFlare CDN IPs to the specified IP, can be used to accelerate CloudFlare's CDN websites."
+msgstr "IP地址映射,将所有CloudFlare CDN IP映射到设置的IP,可用于加速CloudFlare的CDN网站。"
+
+msgid "IP alias"
+msgstr "IP别名"
+
+msgid "IPset Name"
+msgstr "IPSet名称"
+
+msgid "IPset name."
+msgstr "IPSet名称。"
+
+msgid ""
+"If a client address is specified, only that client will apply this rule. You "
+"can enter an IP address, such as 1.2.3.4, or a MAC address, such as aa:bb:cc:"
+"dd:ee:ff."
+msgstr "如果指定了客户端,那么对应的客户端会应用相应的规则,可以输出IP地址,如:1.2.3.4,或MAC地址,如:aa:bb:cc:dd:ee:ff。"
+
+msgid "Invalid server address: %s"
+msgstr "无效的服务器地址:%s"
+
+msgid "Main DNS Server"
+msgstr "主DNS服务"
+
+msgid "NFTset Name"
+msgstr "NFTset名称"
+
+msgid "NFTset name format error, format: [#[4|6]:[family#table#set]]"
+msgstr "NFTset名称格式错误,格式:[#[4|6]:[family#table#set]]"
+
+msgid "NFTset name, format: [#[4|6]:[family#table#set]]"
+msgstr "NFTSet名称错误,格式:[#[4|6]:[family#table#set]]"
+
+msgid "NOT RUNNING"
+msgstr "未运行"
+
+msgid "None"
+msgstr "无"
+
+msgid "Parental Control Domain File"
+msgstr "家长控制域名列表文件"
+
+msgid "Parental Control Settings"
+msgstr "家长控制设置"
+
+msgid "Parental Control Upstream Server"
+msgstr "家长控制上游服务器"
+
+msgid "Parental control feature is only available in Main DNS mode."
+msgstr "家长控制功能仅在作为主DNS时才可用。"
+
+msgid "Please check the system logs and check if the configuration is valid."
+msgstr "请检查系统日志,并确保配置正确。"
+
+msgid "RUNNING"
+msgstr "运行中"
+
+msgid "Restart"
+msgstr "重启"
+
+msgid "Restart Service"
+msgstr "重启服务"
+
+msgid "Set the IP addresses for accelerating CloudFlare CDN."
+msgstr "设置CloudFlare cdn加速。"
+
+msgid "Set the file for blocking ad domain names."
+msgstr "设置屏蔽的域名。"
+
+msgid "Settings"
+msgstr "设置"
+
+msgid "SmartDNS"
+msgstr "SmartDNS"
+
+msgid "SmartDNS official website"
+msgstr "SmartDNS官方网站。"
+
+msgid "SmartDNS Lite"
+msgstr "SmartDNS轻量版"
+
+msgid "Smartdns server mode."
+msgstr "smartdns服务器模式。"
+
+msgid "Smartdns server port."
+msgstr "smartdns服务器端口。"
+
+msgid "Smartdns speed check mode."
+msgstr "Smartdns测速模式设置。"
+
+msgid "Speed Check Mode"
+msgstr "测速模式"
+
+msgid "Speed check mode is invalid."
+msgstr "测速模式无效。"
+
+msgid "TCP port is empty"
+msgstr "TCP端口为空"
+
+msgid "Upload CloudFlare cdn ip list file, please refer to https://www.cloudflare.com/ips"
+msgstr "上传CloudFlare CDN IP列表文件,请参考https://www.cloudflare.com/ips"
+
+msgid "Upload domain list file for matching these rules, if not specified, the rules will be applied to all domains."
+msgstr "上传域名列表文件以匹配规则,未设置任何域名时,规则将应用到所有域名。"
+
+msgid "Upstream DNS Server"
+msgstr "上游DNS服务器"
+
+msgid "Upstream Server"
+msgstr "上游服务器"
+
+msgid "Upstream server for specific domain. If not specified, the default server will be used."
+msgstr "指定对应域名的上游服务器,未指定服务器时,将使用默认的服务器。"
+
+msgid ""
+"Upstream server with parental control feature. If not specified, the default "
+"server will be used."
+msgstr "启用家长控制且未配置上游服务器时,将使用默认服务器。"
+
+msgid "Upstream servers, format: [udp://|tcp://|tls://|https://][ip]."
+msgstr "上游服务器,格式:[udp://|tcp://|tls://|https://][ip]。"
+
+msgid "default"
+msgstr "默认"
+
+msgid "ipset name format error, format: [#[4|6]:]ipsetname"
+msgstr "ipset名称格式错误,格式为:[#[4|6]:]ipsetname"
+
+msgid "open website"
+msgstr "打开网站"
+
+msgid "smartdns custom settings"
+msgstr "smartdns自定义设置"

+ 3 - 0
package/luci-lite/files/root/etc/config/smartdns-lite

@@ -0,0 +1,3 @@
+config 'smartdns-lite'
+	option 'enabled' '0'
+	

+ 228 - 0
package/luci-lite/files/root/etc/init.d/smartdns-lite

@@ -0,0 +1,228 @@
+#!/bin/sh /etc/rc.common
+#
+# Copyright (C) 2018-2024 Ruilin Peng (Nick) <[email protected]>.
+#
+# smartdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# smartdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+START=19
+STOP=82
+NAME=smartdns-lite
+USE_PROCD=1
+
+SMARTDNS_CONF_DIR="/etc/smartdns"
+SMARTDNS_CONF_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/conf.d"
+SMARTDNS_VAR_CONF_DIR="/var/etc/smartdns"
+SMARTDNS_CONF="$SMARTDNS_VAR_CONF_DIR/smartdns-lite.conf"
+CUSTOM_CONF="$SMARTDNS_CONF_DIR/custom.conf"
+SMARTDNS_CONF_TMP="${SMARTDNS_CONF}.tmp"
+
+conf_append()
+{
+	echo "$1 $2" >> $SMARTDNS_CONF_TMP
+}
+
+client_rule_addr_append()
+{
+	conf_append "client-rules" "$1"
+}
+
+servers_append()
+{
+	conf_append "server" "$1 $server_options"
+}
+
+load_parental_control_rules()
+{
+	local section="$1"
+	local adblock_set_name="$2"
+	local block_domain_set_file=""
+	local client_set_name="pc-client-address-$section"
+	local block_set_name="pc-block-domain-$section"
+	local server_options="-e"
+
+	config_get_bool pc_enabled "$section" "pc_enabled" "0"
+	[ "$pc_enabled" != "1" ] && return
+
+	conf_append "group-begin" "parental-control-${section}"
+
+	config_get pc_client_addr_file "$section" "pc_client_addr_file" ""
+	[ -e "$pc_client_addr_file" ] && {
+		conf_append "ip-set" "-name ${client_set_name} -file '$pc_client_addr_file'"
+		client_rule_addr_append "ip-set:${client_set_name}"
+	}
+
+	config_list_foreach "$section" "pc_client_addr" client_rule_addr_append
+
+	config_list_foreach "$section" "pc_servers" servers_append
+
+	config_get pc_block_file "$section" "pc_block_file" ""
+	[ -e "$pc_block_file" ] && {
+		conf_append "domain-set" "-name ${block_set_name} -file '$pc_block_file'"
+		conf_append "domain-rules" "/domain-set:${block_set_name}/ -address #"
+	}
+
+	[ ! -z "$adblock_set_name" ] && {
+		conf_append "domain-rules" "/domain-set:${adblock_set_name}/ -address #"
+	}
+
+	conf_append "group-end"
+}
+
+load_domain_rules()
+{
+	local section="$1"
+	local domain_set_args=""
+	local domain_set_name="rules-domain-set-$section"
+	local domain_rule_name="rules-domain-group-$section"
+	local as_group=0;
+	local qtype_soa_list=""
+	local server_options=""
+
+	config_get_bool rules_enabled "$section" "rules_enabled" "0"
+	[ "$rules_enabled" != "1" ] && return
+
+	config_list_foreach "$section" "rules_servers" servers_append
+
+	config_get rules_domain_file "$section" "rules_domain_file" ""
+	[ -e "$rules_domain_file" ] && {
+		conf_append "group-begin" "${domain_rule_name}"
+		conf_append "domain-set" "-name ${domain_set_name} -file '$rules_domain_file'"
+		conf_append "group-match" "-domain ${domain_set_name}"
+		conf_append "force-qtype-SOA" "-"
+		server_options="-e"
+		as_group="1"
+	}
+
+	config_get rules_speed_check_mode "$section" "rules_speed_check_mode" ""
+	[ ! -z "$rules_speed_check_mode" ] && conf_append "speed-check-mode" "$rules_speed_check_mode"
+
+	config_get rules_force_aaaa_soa "$section" "rules_force_aaaa_soa" "0"
+	[ "$rules_force_aaaa_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 28"
+
+	config_get rules_force_https_soa "$section" "rules_force_https_soa" "1"
+	[ "$rules_force_https_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 65"
+
+	[ ! -z "$qtype_soa_list" ] && conf_append "force-qtype-SOA" "$qtype_soa_list"
+
+	config_get ipset_name "$section" "ipset_name" ""
+	[ -z "$ipset_name" ] || conf_append "ipset" "$ipset_name"
+
+	config_get nftset_name "$section" "nftset_name" ""
+	[ -z "$nftset_name" ] || conf_append "nftset" "$nftset_name"
+
+	[ "$as_group" = "1" ] && {
+		conf_append "group-end"
+	}
+}
+
+cloudflare_cdn_alias()
+{
+	conf_append "ip-alias" "$1 ip-set:$ipset_set_name"
+}
+
+load_cloudflare_cdn_accelerate()
+{
+	local section="$1"
+	local ipset_set_name="cloudflare-ip-set-$section"
+
+	config_get_bool cloudflare_enabled "$section" "cloudflare_enabled" "0"
+	[ "$cloudflare_enabled" != "1" ] && return
+
+	config_get cloudflare_cdn_ip_file "$section" "cloudflare_cdn_ip_file" ""
+	[ ! -e "$cloudflare_cdn_ip_file" ] && return
+
+	conf_append "ip-set" "-name ${ipset_set_name} -file '$cloudflare_cdn_ip_file'"
+	config_list_foreach "$section" "cloudflare_ip_alias" cloudflare_cdn_alias
+}
+
+unload_service()
+{
+	:
+}
+
+load_service()
+{
+	local section="$1"
+	args=""
+	local device=""
+	local adblock_set_name=""
+	local is_auto_set="0"
+
+	mkdir -p $SMARTDNS_VAR_CONF_DIR
+	rm -f $SMARTDNS_CONF_TMP
+
+	config_get_bool enabled "$section" "enabled" '0'
+	[ "$enabled" != "1" ] && {
+		uci -q set smartdns.@smartdns[0].enabled="0"
+		uci commit smartdns
+		/etc/init.d/smartdns reload
+		return
+	}
+
+	config_get port "$section" "port" "53"
+	config_get server_mode "$section" "server_mode" "tcp_only"
+	config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "0"
+
+	[ "$server_mode" = "main" ] && {
+		port="53"
+	}
+	
+	config_list_foreach "$section" "servers" servers_append
+
+	config_get ad_block_file "$section" "ad_block_file" ""
+	[ -e "$ad_block_file" ] && {
+		adblock_set_name="adblock-block-$section"
+		conf_append "domain-set" "-name ${adblock_set_name} -file '$ad_block_file'"
+		conf_append "domain-rules" "/domain-set:${adblock_set_name}/ -address #"
+	}
+
+	load_cloudflare_cdn_accelerate "$section"
+
+	load_parental_control_rules "$section" "$adblock_set_name"
+
+	load_domain_rules "$section"
+
+	uci -q set smartdns.@smartdns[0].enabled="1"
+	uci -q set smartdns.@smartdns[0].port="$port"
+	uci -q set smartdns.@smartdns[0].auto_set_dnsmasq="$auto_set_dnsmasq"
+	uci -q del_list smartdns.@smartdns[0].conf_files="$SMARTDNS_CONF"
+	uci -q add_list smartdns.@smartdns[0].conf_files="$SMARTDNS_CONF"
+
+	touch $SMARTDNS_CONF_TMP
+	mv $SMARTDNS_CONF_TMP $SMARTDNS_CONF
+	uci commit smartdns
+	/etc/init.d/smartdns reload
+}
+
+service_triggers() {
+	procd_add_reload_trigger smartdns-lite
+}
+
+service_stopped()
+{
+	config_load "smartdns-lite"
+	config_foreach unload_service "smartdns-lite"
+}
+
+start_service()
+{
+	config_load "smartdns-lite"
+	config_foreach load_service "smartdns-lite"
+}
+
+reload_service()
+{
+	stop
+	start
+}

+ 13 - 0
package/luci-lite/files/root/usr/share/luci/menu.d/luci-app-smartdns-lite.json

@@ -0,0 +1,13 @@
+{
+	"admin/services/smartdns-lite": {
+		"title": "SmartDNS Lite",
+		"action": {
+			"type": "view",
+			"path": "smartdns-lite/smartdns-lite"
+		},
+		"depends": {
+			"acl": [ "luci-app-smartdns-lite" ],
+			"uci": { "smartdns-lite": true }
+		}
+	}
+}

+ 22 - 0
package/luci-lite/files/root/usr/share/rpcd/acl.d/luci-app-smartdns-lite.json

@@ -0,0 +1,22 @@
+{
+	"luci-app-smartdns-lite": {
+		"description": "Grant access to LuCI app smartdns",
+		"read": {
+			"file": {
+				"/etc/smartdns/*": [ "read" ]
+			},
+			"ubus": {
+				"service": [ "list" ]
+			},
+			"uci": [ "smartdns-lite" ]
+		},
+		"write": {
+			"file": {
+				"/etc/smartdns/*": [ "write" ],
+				"/etc/init.d/smartdns-lite restart": [ "exec" ],
+				"/etc/init.d/smartdns-lite updatefiles": [ "exec" ]
+			},
+			"uci": [ "smartdns-lite" ]
+		}
+	}
+}

+ 425 - 0
package/luci-lite/files/root/www/luci-static/resources/view/smartdns-lite/smartdns-lite.js

@@ -0,0 +1,425 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * smartdns is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * smartdns is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+'use strict';
+'require fs';
+'require uci';
+'require form';
+'require view';
+'require poll';
+'require rpc';
+'require ui';
+
+var conf = 'smartdns';
+var callServiceList = rpc.declare({
+	object: 'service',
+	method: 'list',
+	params: ['name'],
+	expect: { '': {} }
+});
+var pollAdded = false;
+
+function getServiceStatus() {
+	return L.resolveDefault(callServiceList(conf), {})
+		.then(function (res) {
+			var is_running = false;
+			try {
+				is_running = res[conf]['instances']['smartdns']['running'];
+			} catch (e) {
+
+			}
+			return is_running;
+		})
+}
+
+function smartdnsServiceStatus() {
+	return Promise.all([
+		getServiceStatus()
+	]);
+}
+
+function smartdnsRenderStatus(res) {
+	var renderHTML = "";
+	var isRunning = res[0];
+
+	var autoSetDnsmasq = uci.get_first('smartdns', 'smartdns', 'auto_set_dnsmasq');
+	var smartdnsPort = uci.get_first('smartdns', 'smartdns', 'port');
+	var smartdnsEnable = uci.get_first('smartdns', 'smartdns', 'enabled');
+	var dnsmasqServer = uci.get_first('dhcp', 'dnsmasq', 'server');
+
+	if (isRunning) {
+		renderHTML += "<span style=\"color:green;font-weight:bold\">SmartDNS - " + _("RUNNING") + "</span>";
+	} else {
+		renderHTML += "<span style=\"color:red;font-weight:bold\">SmartDNS - " + _("NOT RUNNING") + "</span>";
+		if (smartdnsEnable === '1') {
+			renderHTML += "<br /><span style=\"color:red;font-weight:bold\">" + _("Please check the system logs and check if the configuration is valid.");
+			renderHTML += "</span>";
+		}
+		return renderHTML;
+	}
+
+	if (autoSetDnsmasq === '1' && smartdnsPort != '53') {
+		var matchLine = "127.0.0.1#" + smartdnsPort;
+
+		uci.unload('dhcp');
+		uci.load('dhcp');
+		if (dnsmasqServer == undefined || dnsmasqServer.indexOf(matchLine) < 0) {
+			renderHTML += "<br /><span style=\"color:red;font-weight:bold\">" + _("Dnsmasq Forwarded To Smartdns Failure") + "</span>";
+		}
+	}
+
+	return renderHTML;
+}
+
+return view.extend({
+	load: function () {
+		return Promise.all([
+			uci.load('dhcp'),
+			uci.load('smartdns'),
+			uci.load('smartdns-lite'),
+		]);
+	},
+	render: function (stats) {
+		var m, s, o;
+
+		m = new form.Map('smartdns-lite', _('SmartDNS Lite'));
+		m.title = _("SmartDNS Lite");
+		m.description = _("A local SmartDNS server for lite users.");
+
+		s = m.section(form.NamedSection, '_status');
+		s.anonymous = true;
+		s.render = function (section_id) {
+			var renderStatus = function () {
+				return L.resolveDefault(smartdnsServiceStatus()).then(function (res) {
+					var view = document.getElementById("service_status");
+					if (view == null) {
+						return;
+					}
+
+					view.innerHTML = smartdnsRenderStatus(res);
+				});
+			}
+
+			if (pollAdded == false) {
+				poll.add(renderStatus, 1);
+				pollAdded = true;
+			}
+
+			return E('div', { class: 'cbi-section' }, [
+				E('div', { id: 'service_status' },
+					_('Collecting data ...'))
+			]);
+		}
+
+		////////////////
+		// Basic;
+		////////////////
+		s = m.section(form.TypedSection, "smartdns-lite", _("Settings"));
+		s.anonymous = true;
+
+		s.tab("settings", _("Basic Settings"));
+		s.tab("parental", _("Parental Control Settings"));
+		s.tab("rules", _("Domain Rules Settings"));
+		s.tab("cloudflare", _("CloudFlare CDN IP Settings"), _("Set the IP addresses for accelerating CloudFlare CDN."));
+		s.tab("custom", _("Custom Settings"));
+
+		o = s.taboption("settings", form.Flag, "enabled", _("Enable"), _("Enable or disable smartdns server"));
+		o.rmempty = false;
+		o.default = o.disabled;
+
+		o = s.taboption("settings", form.DynamicList, "servers", _("Upstream Server"),
+			_("Upstream servers, format: [udp://|tcp://|tls://|https://][ip]."));
+		o.rempty = true
+		o.rmempty = true;
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			var values = value.split(/\s+/);
+			for (var i = 0; i < values.length; i++) {
+				if (!values[i].match(/^(https?|udp|tcp|tls|quic):\/\/[0-9a-zA-Z\.\[\]:]+(\/[^\s]*)?$/)) {
+					return _('Invalid server address: %s').format(values[i]);
+				}
+			}
+			return true;
+		};
+
+		o = s.taboption("settings", form.FileUpload, "ad_block_file", _("AD Block Domain List File"),
+			_("Set the file for blocking ad domain names."));
+		o.rmempty = true
+		o.datatype = "file"
+		o.rempty = true
+		o.editable = true
+		o.root_directory = "/etc/smartdns/domain-set"
+
+		o = s.taboption("settings", form.ListValue, "server_mode", _("DNS Server Mode"), _("Smartdns server mode."));
+		o.rmempty = false;
+		o.value("main", _("Main DNS Server"));
+		o.value("upstream", _("Upstream DNS Server"));
+
+		o = s.taboption("settings", form.Value, "port", _("DNS Server Port"), _("Smartdns server port."));
+		o.rmempty = true
+		o.default = 6053;
+		o.datatype = "port";
+		o.depends("server_mode", "upstream");
+
+		o = s.taboption("parental", form.Flag, "pc_enabled", _("Enable"), _("Enable or disable smartdns server"));
+		o.rmempty = false;
+		o.default = o.disabled;
+		o.validate = function (section_id, value) {
+			var v = this.map.lookupOption('pc_enabled', section_id)[0];
+			if (v.formvalue(section_id) == 0) {
+				return true;
+			}
+
+			var server_mode = this.map.lookupOption('server_mode', section_id)[0];
+			if (server_mode.formvalue(section_id) != "main") {
+				return _("Parental control feature is only available in Main DNS mode.");
+			}
+
+			return true;
+		}
+
+		o = s.taboption("parental", form.DynamicList, "pc_client_addr", _("Client Address"),
+			_("If a client address is specified, only that client will apply this rule. You can enter an IP address, such as 1.2.3.4, or a MAC address, such as aa:bb:cc:dd:ee:ff."));
+		o.rempty = true
+		o.rmempty = true;
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			if (value.match(/^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$/)) {
+				return true;
+			}
+
+			if (value.match(/^([a-fA-F0-9]*:){1,7}[a-fA-F0-9]*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/)) {
+				return true;
+			}
+
+			if (value.match(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/)) {
+				return true;
+			}
+
+			return _("Client address format error, please input ip adress or mac address.");
+		}
+
+		o = s.taboption("parental", form.DynamicList, "pc_servers", _("Parental Control Upstream Server"),
+			_("Upstream server with parental control feature. If not specified, the default server will be used."));
+		o.rempty = true
+		o.rmempty = true;
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			var values = value.split(/\s+/);
+			for (var i = 0; i < values.length; i++) {
+				if (!values[i].match(/^(https?|udp|tcp|tls|quic):\/\/[0-9a-zA-Z\.\[\]:]+(\/[^\s]*)?$/)) {
+					return _('Invalid server address: %s').format(values[i]);
+				}
+			}
+			return true;
+		};
+
+		o = s.taboption("parental", form.FileUpload, "pc_block_file", _("Parental Control Domain File"),
+			_("Block Domain List File for Parental Control."));
+		o.rmempty = true
+		o.datatype = "file"
+		o.rempty = true
+		o.editable = true
+		o.root_directory = "/etc/smartdns/domain-set"
+
+		o = s.taboption("rules", form.Flag, "rules_enabled", _("Enable"), _("Enable or disable domain rules."));
+		o.rmempty = false;
+		o.default = o.disabled;
+
+		o = s.taboption("rules", form.FileUpload, "rules_domain_file", _("Domain List File"),
+			_("Upload domain list file for matching these rules, if not specified, the rules will be applied to all domains."));
+		o.rmempty = true
+		o.datatype = "file"
+		o.rempty = true
+		o.editable = true
+		o.root_directory = "/etc/smartdns/domain-set"
+
+		o = s.taboption("rules", form.DynamicList, "rules_servers", _("Upstream Server"), 
+			_("Upstream server for specific domain. If not specified, the default server will be used."));
+		o.rempty = true
+		o.rmempty = true;
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			var values = value.split(/\s+/);
+			for (var i = 0; i < values.length; i++) {
+				if (!values[i].match(/^(https?|udp|tcp|tls|quic):\/\/[0-9a-zA-Z\.\[\]:]+(\/[^\s]*)?$/)) {
+					return _('Invalid server address: %s').format(values[i]);
+				}
+			}
+			return true;
+		};
+
+		o = s.taboption("rules", form.Value, "rules_speed_check_mode", _("Speed Check Mode"), _("Smartdns speed check mode."));
+		o.rmempty = true;
+		o.placeholder = "default";
+		o.value("", _("default"));
+		o.value("ping,tcp:80,tcp:443");
+		o.value("ping,tcp:443,tcp:80");
+		o.value("tcp:80,tcp:443,ping");
+		o.value("tcp:443,tcp:80,ping");
+		o.value("none", _("None"));
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			if (value == "none") {
+				return true;
+			}
+
+			var check_mode = value.split(",")
+			for (var i = 0; i < check_mode.length; i++) {
+				if (check_mode[i] == "ping") {
+					continue;
+				}
+
+				if (check_mode[i].indexOf("tcp:") == 0) {
+					var port = check_mode[i].split(":")[1];
+					if (port == "") {
+						return _("TCP port is empty");
+					}
+
+					continue;
+				}
+
+				return _("Speed check mode is invalid.");
+			}
+
+			return true;
+		}
+
+		// Force AAAA SOA
+		o = s.taboption("rules", form.Flag, "rules_force_aaaa_soa", _("Force AAAA SOA"), _("Force AAAA SOA."));
+		o.rmempty = true;
+		o.default = o.disabled;
+
+		// Force HTTPS SOA
+		o = s.taboption("rules", form.Flag, "rules_force_https_soa", _("Force HTTPS SOA"), _("Force HTTPS SOA."));
+		o.rmempty = true;
+		o.default = o.enabled;
+
+		o = s.taboption("rules", form.Value, "rules_ipset_name", _("IPset Name"), _("IPset name."));
+		o.rmempty = true;
+		o.datatype = "string";
+		o.rempty = true;
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			var ipset = value.split(",")
+			for (var i = 0; i < ipset.length; i++) {
+				if (!ipset[i].match(/^(#[4|6]:)?[a-zA-Z0-9\-_]+$/)) {
+					return _("ipset name format error, format: [#[4|6]:]ipsetname");
+				}
+			}
+
+			return true;
+		}
+
+		o = s.taboption("rules", form.Value, "rules_nftset_name", _("NFTset Name"), _("NFTset name, format: [#[4|6]:[family#table#set]]"));
+		o.rmempty = true;
+		o.datatype = "string";
+		o.rempty = true;
+		o.validate = function (section_id, value) {
+			if (value == "") {
+				return true;
+			}
+
+			var nftset = value.split(",")
+			for (var i = 0; i < nftset.length; i++) {
+				if (!nftset[i].match(/^#[4|6]:[a-zA-Z0-9\-_]+#[a-zA-Z0-9\-_]+#[a-zA-Z0-9\-_]+$/)) {
+					return _("NFTset name format error, format: [#[4|6]:[family#table#set]]");
+				}
+			}
+
+			return true;
+		}
+
+		o = s.taboption("cloudflare", form.Flag, "cloudflare_enabled", _("Enable"), 
+			_("Enable or disable cloudflare cdn ip accelerating."));
+		o.rmempty = false;
+		o.default = o.disabled;
+
+		o = s.taboption("cloudflare", form.FileUpload, "cloudflare_cdn_ip_file", _("CloudFlare CDN IP File"),
+			_("Upload CloudFlare cdn ip list file, please refer to https://www.cloudflare.com/ips"));
+		o.rmempty = true
+		o.datatype = "file"
+		o.rempty = true
+		o.modalonly = true;
+		o.root_directory = "/etc/smartdns/ip-set"
+
+		o = s.taboption("cloudflare", form.DynamicList, "cloudflare_ip_alias", _("IP alias"),
+		 	_("IP Address Mapping, mapping all CloudFlare CDN IPs to the specified IP, can be used to accelerate CloudFlare's CDN websites."));
+		o.rmempty = true;
+		o.datatype = 'ipaddr("nomask")';
+		o.modalonly = true;
+
+		///////////////////////////////////////
+		// custom settings;
+		///////////////////////////////////////
+		o = s.taboption("custom", form.TextValue, "custom_conf",
+			"", _("smartdns custom settings"));
+		o.rows = 20;
+		o.cfgvalue = function (section_id) {
+			return fs.trimmed('/etc/smartdns/custom.conf');
+		};
+		o.write = function (section_id, formvalue) {
+			return this.cfgvalue(section_id).then(function (value) {
+				if (value == formvalue) {
+					return
+				}
+				return fs.write('/etc/smartdns/custom.conf', formvalue.trim().replace(/\r\n/g, '\n') + '\n');
+			});
+		};
+
+		o = s.taboption("custom", form.Button, "web");
+		o.title = _("SmartDNS official website");
+		o.inputtitle = _("open website");
+		o.inputstyle = "apply";
+		o.onclick = function () {
+			window.open("https://pymumu.github.io/smartdns", '_blank');
+		};
+
+		o = s.taboption("custom", form.DummyValue, "_restart", _("Restart Service"));
+		o.renderWidget = function () {
+			return E('button', {
+				'class': 'btn cbi-button cbi-button-apply',
+				'id': 'btn_restart',
+				'click': ui.createHandlerFn(this, function () {
+					return fs.exec('/etc/init.d/smartdns-lite', ['restart'])
+						.catch(function (e) { ui.addNotification(null, E('p', e.message), 'error') });
+				})
+			}, [_("Restart")]);
+		}
+		return m.render();
+	}
+});

+ 139 - 0
package/luci-lite/make.sh

@@ -0,0 +1,139 @@
+#!/bin/sh
+#
+# Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+#
+# smartdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# smartdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+CURR_DIR=$(cd $(dirname $0);pwd)
+
+VER="`date +"1.%Y.%m.%d-%H%M"`"
+SMARTDNS_DIR=$CURR_DIR/../../
+PO2LMO=
+
+showhelp()
+{
+	echo "Usage: make [OPTION]"
+	echo "Options:"
+	echo " -o               output directory."
+	echo " --arch           archtecture."
+	echo " --ver            version."
+	echo " -h               show this message."
+}
+
+build_tool()
+{
+	make -C $ROOT/tool/po2lmo -j 
+	PO2LMO="$ROOT/tool/po2lmo/src/po2lmo"
+
+}
+
+clean_tool()
+{
+	make -C $ROOT/tool/po2lmo clean
+}
+
+build()
+{
+
+	ROOT=/tmp/luci-app-smartdns
+	rm -fr $ROOT
+
+	mkdir -p $ROOT
+	cp $CURR_DIR/* $ROOT/ -af
+	cp $CURR_DIR/../tool $ROOT/ -af
+	cd $ROOT/
+	build_tool
+	
+	mkdir $ROOT/root/usr/lib/lua/luci -p
+	mkdir $ROOT/root/usr/share/rpcd/acl.d/ -p
+	cp $ROOT/files/luci/i18n $ROOT/root/usr/lib/lua/luci/ -avf
+
+	#Generate Language
+	$PO2LMO $ROOT/files/luci/i18n/smartdns-lite.zh-cn.po $ROOT/root/usr/lib/lua/luci/i18n/smartdns-lite.zh-cn.lmo
+	rm $ROOT/root/usr/lib/lua/luci/i18n/smartdns-lite.zh-cn.po
+	chmod +x $ROOT/files/root/etc/init.d/smartdns-lite
+
+	cp $ROOT/files/root/* $ROOT/root/ -avf
+	INST_SIZE="`du -sb $ROOT/root/ | awk '{print $1}'`"
+	
+	sed -i "s/^Architecture.*/Architecture: all/g" $ROOT/control/control
+	sed -i "s/Version:.*/Version: $VER/" $ROOT/control/control
+
+	if [ ! -z "$INST_SIZE" ]; then
+		echo "Installed-Size: $INST_SIZE" >> $ROOT/control/control
+	fi
+
+	cd $ROOT/control
+	chmod +x *
+	tar zcf ../control.tar.gz ./
+	cd $ROOT
+
+	tar zcf $ROOT/data.tar.gz -C root .
+	tar zcf $OUTPUTDIR/luci-app-smartdns-lite.$VER.$FILEARCH.ipk ./control.tar.gz ./data.tar.gz ./debian-binary
+
+	rm -fr $ROOT/
+}
+
+main()
+{
+	OPTS=`getopt -o o:h --long arch:,ver:,filearch: \
+		-n  "" -- "$@"`
+
+	if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
+
+	# Note the quotes around `$TEMP': they are essential!
+	eval set -- "$OPTS"
+
+	while true; do
+		case "$1" in
+		--arch)
+			ARCH="$2"
+			shift 2;;
+		--filearch)
+			FILEARCH="$2"
+			shift 2;;
+		--ver)
+			VER="$2"
+			shift 2;;
+		-o )
+			OUTPUTDIR="$2"
+			shift 2;;
+		-h | --help )
+			showhelp
+			return 0
+			shift ;;
+		-- ) shift; break ;;
+		* ) break ;;
+		esac
+	done
+
+	if [ -z "$ARCH" ]; then
+		echo "please input arch."
+		return 1;
+	fi
+
+	if [ -z "$FILEARCH" ]; then
+		FILEARCH=$ARCH
+	fi
+
+	if [ -z "$OUTPUTDIR" ]; then
+		OUTPUTDIR=$CURR_DIR;
+	fi
+
+	build
+}
+
+main $@
+exit $?
+
+

+ 1 - 1
package/luci/control/postinst

@@ -16,6 +16,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 [ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0
-[ -x ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
+[ -e ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
 . ${IPKG_INSTROOT}/lib/functions.sh
 default_postinst $0 $@

+ 0 - 0
package/luci/control/postinst-pkg


+ 1 - 1
package/luci/control/prerm

@@ -15,6 +15,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-[ -x ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
+[ -e ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
 . ${IPKG_INSTROOT}/lib/functions.sh
 default_prerm $0 $@

+ 1 - 0
package/luci/make.sh

@@ -50,6 +50,7 @@ build()
 
 	mkdir -p $ROOT
 	cp $CURR_DIR/* $ROOT/ -af
+	cp $CURR_DIR/../tool $ROOT/ -af
 	cd $ROOT/
 	build_tool
 	

+ 0 - 12
package/luci/tool/po2lmo/Makefile

@@ -1,12 +0,0 @@
-
-INSTALL = install
-PREFIX  = /usr/bin
-
-po2lmo: src/po2lmo.o src/template_lmo.o
-	$(CC) $(LDFLAGS) -o src/po2lmo src/po2lmo.o src/template_lmo.o
-
-install:
-	$(INSTALL) -m 755 src/po2lmo $(PREFIX)
-
-clean:
-	$(RM) src/po2lmo src/*.o

+ 0 - 247
package/luci/tool/po2lmo/src/po2lmo.c

@@ -1,247 +0,0 @@
-/*
- * lmo - Lua Machine Objects - PO to LMO conversion tool
- *
- *   Copyright (C) 2009-2012 Jo-Philipp Wich <[email protected]>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_lmo.h"
-
-static void die(const char *msg)
-{
-	fprintf(stderr, "Error: %s\n", msg);
-	exit(1);
-}
-
-static void usage(const char *name)
-{
-	fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
-	exit(1);
-}
-
-static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
-{
-	if( fwrite(ptr, size, nmemb, stream) == 0 )
-		die("Failed to write stdout");
-}
-
-static int extract_string(const char *src, char *dest, int len)
-{
-	int pos = 0;
-	int esc = 0;
-	int off = -1;
-
-	for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
-	{
-		if( (off == -1) && (src[pos] == '"') )
-		{
-			off = pos + 1;
-		}
-		else if( off >= 0 )
-		{
-			if( esc == 1 )
-			{
-				switch (src[pos])
-				{
-				case '"':
-				case '\\':
-					off++;
-					break;
-				}
-				dest[pos-off] = src[pos];
-				esc = 0;
-			}
-			else if( src[pos] == '\\' )
-			{
-				dest[pos-off] = src[pos];
-				esc = 1;
-			}
-			else if( src[pos] != '"' )
-			{
-				dest[pos-off] = src[pos];
-			}
-			else
-			{
-				dest[pos-off] = '\0';
-				break;
-			}
-		}
-	}
-
-	return (off > -1) ? strlen(dest) : -1;
-}
-
-static int cmp_index(const void *a, const void *b)
-{
-	uint32_t x = ((const lmo_entry_t *)a)->key_id;
-	uint32_t y = ((const lmo_entry_t *)b)->key_id;
-
-	if (x < y)
-		return -1;
-	else if (x > y)
-		return 1;
-
-	return 0;
-}
-
-static void print_uint32(uint32_t x, FILE *out)
-{
-	uint32_t y = htonl(x);
-	print(&y, sizeof(uint32_t), 1, out);
-}
-
-static void print_index(void *array, int n, FILE *out)
-{
-	lmo_entry_t *e;
-
-	qsort(array, n, sizeof(*e), cmp_index);
-
-	for (e = array; n > 0; n--, e++)
-	{
-		print_uint32(e->key_id, out);
-		print_uint32(e->val_id, out);
-		print_uint32(e->offset, out);
-		print_uint32(e->length, out);
-	}
-}
-
-int main(int argc, char *argv[])
-{
-	char line[4096];
-	char key[4096];
-	char val[4096];
-	char tmp[4096];
-	int state  = 0;
-	int offset = 0;
-	int length = 0;
-	int n_entries = 0;
-	void *array = NULL;
-	lmo_entry_t *entry = NULL;
-	uint32_t key_id, val_id;
-
-	FILE *in;
-	FILE *out;
-
-	if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
-		usage(argv[0]);
-
-	memset(line, 0, sizeof(key));
-	memset(key, 0, sizeof(val));
-	memset(val, 0, sizeof(val));
-
-	while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
-	{
-		if( state == 0 && strstr(line, "msgid \"") == line )
-		{
-			switch(extract_string(line, key, sizeof(key)))
-			{
-				case -1:
-					die("Syntax error in msgid");
-				case 0:
-					state = 1;
-					break;
-				default:
-					state = 2;
-			}
-		}
-		else if( state == 1 || state == 2 )
-		{
-			if( strstr(line, "msgstr \"") == line || state == 2 )
-			{
-				switch(extract_string(line, val, sizeof(val)))
-				{
-					case -1:
-						state = 4;
-						break;
-					default:
-						state = 3;
-				}
-			}
-			else
-			{
-				switch(extract_string(line, tmp, sizeof(tmp)))
-				{
-					case -1:
-						state = 2;
-						break;
-					default:
-						strcat(key, tmp);
-				}
-			}
-		}
-		else if( state == 3 )
-		{
-			switch(extract_string(line, tmp, sizeof(tmp)))
-			{
-				case -1:
-					state = 4;
-					break;
-				default:
-					strcat(val, tmp);
-			}
-		}
-
-		if( state == 4 )
-		{
-			if( strlen(key) > 0 && strlen(val) > 0 )
-			{
-				key_id = sfh_hash(key, strlen(key));
-				val_id = sfh_hash(val, strlen(val));
-
-				if( key_id != val_id )
-				{
-					n_entries++;
-					array = realloc(array, n_entries * sizeof(lmo_entry_t));
-					entry = (lmo_entry_t *)array + n_entries - 1;
-
-					if (!array)
-						die("Out of memory");
-
-					entry->key_id = key_id;
-					entry->val_id = val_id;
-					entry->offset = offset;
-					entry->length = strlen(val);
-
-					length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
-
-					print(val, length, 1, out);
-					offset += length;
-				}
-			}
-
-			state = 0;
-			memset(key, 0, sizeof(key));
-			memset(val, 0, sizeof(val));
-		}
-
-		memset(line, 0, sizeof(line));
-	}
-
-	print_index(array, n_entries, out);
-
-	if( offset > 0 )
-	{
-		print_uint32(offset, out);
-		fsync(fileno(out));
-		fclose(out);
-	}
-	else
-	{
-		fclose(out);
-		unlink(argv[2]);
-	}
-
-	fclose(in);
-	return(0);
-}

+ 0 - 328
package/luci/tool/po2lmo/src/template_lmo.c

@@ -1,328 +0,0 @@
-/*
- * lmo - Lua Machine Objects - Base functions
- *
- *   Copyright (C) 2009-2010 Jo-Philipp Wich <[email protected]>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_lmo.h"
-
-/*
- * Hash function from http://www.azillionmonkeys.com/qed/hash.html
- * Copyright (C) 2004-2008 by Paul Hsieh
- */
-
-uint32_t sfh_hash(const char *data, int len)
-{
-	uint32_t hash = len, tmp;
-	int rem;
-
-	if (len <= 0 || data == NULL) return 0;
-
-	rem = len & 3;
-	len >>= 2;
-
-	/* Main loop */
-	for (;len > 0; len--) {
-		hash  += sfh_get16(data);
-		tmp    = (sfh_get16(data+2) << 11) ^ hash;
-		hash   = (hash << 16) ^ tmp;
-		data  += 2*sizeof(uint16_t);
-		hash  += hash >> 11;
-	}
-
-	/* Handle end cases */
-	switch (rem) {
-		case 3: hash += sfh_get16(data);
-			hash ^= hash << 16;
-			hash ^= data[sizeof(uint16_t)] << 18;
-			hash += hash >> 11;
-			break;
-		case 2: hash += sfh_get16(data);
-			hash ^= hash << 11;
-			hash += hash >> 17;
-			break;
-		case 1: hash += *data;
-			hash ^= hash << 10;
-			hash += hash >> 1;
-	}
-
-	/* Force "avalanching" of final 127 bits */
-	hash ^= hash << 3;
-	hash += hash >> 5;
-	hash ^= hash << 4;
-	hash += hash >> 17;
-	hash ^= hash << 25;
-	hash += hash >> 6;
-
-	return hash;
-}
-
-uint32_t lmo_canon_hash(const char *str, int len)
-{
-	char res[4096];
-	char *ptr, prev;
-	int off;
-
-	if (!str || len >= sizeof(res))
-		return 0;
-
-	for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
-	{
-		if (isspace(*str))
-		{
-			if (!isspace(prev))
-				*ptr++ = ' ';
-		}
-		else
-		{
-			*ptr++ = *str;
-		}
-	}
-
-	if ((ptr > res) && isspace(*(ptr-1)))
-		ptr--;
-
-	return sfh_hash(res, ptr - res);
-}
-
-lmo_archive_t * lmo_open(const char *file)
-{
-	int in = -1;
-	uint32_t idx_offset = 0;
-	struct stat s;
-
-	lmo_archive_t *ar = NULL;
-
-	if (stat(file, &s) == -1)
-		goto err;
-
-	if ((in = open(file, O_RDONLY)) == -1)
-		goto err;
-
-	if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
-	{
-		memset(ar, 0, sizeof(*ar));
-
-		ar->fd     = in;
-		ar->size = s.st_size;
-
-		fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
-
-		if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
-			goto err;
-
-		idx_offset = ntohl(*((const uint32_t *)
-		                     (ar->mmap + ar->size - sizeof(uint32_t))));
-
-		if (idx_offset >= ar->size)
-			goto err;
-
-		ar->index  = (lmo_entry_t *)(ar->mmap + idx_offset);
-		ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
-		ar->end    = ar->mmap + ar->size;
-
-		return ar;
-	}
-
-err:
-	if (in > -1)
-		close(in);
-
-	if (ar != NULL)
-	{
-		if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
-			munmap(ar->mmap, ar->size);
-
-		free(ar);
-	}
-
-	return NULL;
-}
-
-void lmo_close(lmo_archive_t *ar)
-{
-	if (ar != NULL)
-	{
-		if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
-			munmap(ar->mmap, ar->size);
-
-		close(ar->fd);
-		free(ar);
-
-		ar = NULL;
-	}
-}
-
-
-lmo_catalog_t *_lmo_catalogs = NULL;
-lmo_catalog_t *_lmo_active_catalog = NULL;
-
-int lmo_load_catalog(const char *lang, const char *dir)
-{
-	DIR *dh = NULL;
-	char pattern[16];
-	char path[PATH_MAX];
-	struct dirent *de = NULL;
-
-	lmo_archive_t *ar = NULL;
-	lmo_catalog_t *cat = NULL;
-
-	if (!lmo_change_catalog(lang))
-		return 0;
-
-	if (!dir || !(dh = opendir(dir)))
-		goto err;
-
-	if (!(cat = malloc(sizeof(*cat))))
-		goto err;
-
-	memset(cat, 0, sizeof(*cat));
-
-	snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
-	snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
-
-	while ((de = readdir(dh)) != NULL)
-	{
-		if (!fnmatch(pattern, de->d_name, 0))
-		{
-			snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
-			ar = lmo_open(path);
-
-			if (ar)
-			{
-				ar->next = cat->archives;
-				cat->archives = ar;
-			}
-		}
-	}
-
-	closedir(dh);
-
-	cat->next = _lmo_catalogs;
-	_lmo_catalogs = cat;
-
-	if (!_lmo_active_catalog)
-		_lmo_active_catalog = cat;
-
-	return 0;
-
-err:
-	if (dh) closedir(dh);
-	if (cat) free(cat);
-
-	return -1;
-}
-
-int lmo_change_catalog(const char *lang)
-{
-	lmo_catalog_t *cat;
-
-	for (cat = _lmo_catalogs; cat; cat = cat->next)
-	{
-		if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
-		{
-			_lmo_active_catalog = cat;
-			return 0;
-		}
-	}
-
-	return -1;
-}
-
-static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
-{
-	unsigned int m, l, r;
-	uint32_t k;
-
-	l = 0;
-	r = ar->length - 1;
-
-	while (1)
-	{
-		m = l + ((r - l) / 2);
-
-		if (r < l)
-			break;
-
-		k = ntohl(ar->index[m].key_id);
-
-		if (k == hash)
-			return &ar->index[m];
-
-		if (k > hash)
-		{
-			if (!m)
-				break;
-
-			r = m - 1;
-		}
-		else
-		{
-			l = m + 1;
-		}
-	}
-
-	return NULL;
-}
-
-int lmo_translate(const char *key, int keylen, char **out, int *outlen)
-{
-	uint32_t hash;
-	lmo_entry_t *e;
-	lmo_archive_t *ar;
-
-	if (!key || !_lmo_active_catalog)
-		return -2;
-
-	hash = lmo_canon_hash(key, keylen);
-
-	for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
-	{
-		if ((e = lmo_find_entry(ar, hash)) != NULL)
-		{
-			*out = ar->mmap + ntohl(e->offset);
-			*outlen = ntohl(e->length);
-			return 0;
-		}
-	}
-
-	return -1;
-}
-
-void lmo_close_catalog(const char *lang)
-{
-	lmo_archive_t *ar, *next;
-	lmo_catalog_t *cat, *prev;
-
-	for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
-	{
-		if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
-		{
-			if (prev)
-				prev->next = cat->next;
-			else
-				_lmo_catalogs = cat->next;
-
-			for (ar = cat->archives; ar; ar = next)
-			{
-				next = ar->next;
-				lmo_close(ar);
-			}
-
-			free(cat);
-			break;
-		}
-	}
-}

+ 0 - 92
package/luci/tool/po2lmo/src/template_lmo.h

@@ -1,92 +0,0 @@
-/*
- * lmo - Lua Machine Objects - General header
- *
- *   Copyright (C) 2009-2012 Jo-Philipp Wich <[email protected]>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#ifndef _TEMPLATE_LMO_H_
-#define _TEMPLATE_LMO_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fnmatch.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <limits.h>
-
-#if (defined(__GNUC__) && defined(__i386__))
-#define sfh_get16(d) (*((const uint16_t *) (d)))
-#else
-#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
-					   +(uint32_t)(((const uint8_t *)(d))[0]) )
-#endif
-
-
-struct lmo_entry {
-	uint32_t key_id;
-	uint32_t val_id;
-	uint32_t offset;
-	uint32_t length;
-} __attribute__((packed));
-
-typedef struct lmo_entry lmo_entry_t;
-
-
-struct lmo_archive {
-	int         fd;
-	int	        length;
-	uint32_t    size;
-	lmo_entry_t *index;
-	char        *mmap;
-	char		*end;
-	struct lmo_archive *next;
-};
-
-typedef struct lmo_archive lmo_archive_t;
-
-
-struct lmo_catalog {
-	char lang[6];
-	struct lmo_archive *archives;
-	struct lmo_catalog *next;
-};
-
-typedef struct lmo_catalog lmo_catalog_t;
-
-
-uint32_t sfh_hash(const char *data, int len);
-uint32_t lmo_canon_hash(const char *data, int len);
-
-lmo_archive_t * lmo_open(const char *file);
-void lmo_close(lmo_archive_t *ar);
-
-
-extern lmo_catalog_t *_lmo_catalogs;
-extern lmo_catalog_t *_lmo_active_catalog;
-
-int lmo_load_catalog(const char *lang, const char *dir);
-int lmo_change_catalog(const char *lang);
-int lmo_translate(const char *key, int keylen, char **out, int *outlen);
-void lmo_close_catalog(const char *lang);
-
-#endif

+ 1 - 1
package/luci-compat/tool/po2lmo/Makefile → package/tool/po2lmo/Makefile

@@ -1,7 +1,7 @@
 
 INSTALL = install
 PREFIX  = /usr/bin
-
+CFLAGS  = -Wall -O2
 po2lmo: src/po2lmo.o src/template_lmo.o
 	$(CC) $(LDFLAGS) -o src/po2lmo src/po2lmo.o src/template_lmo.o
 

+ 0 - 0
package/luci-compat/tool/po2lmo/src/po2lmo.c → package/tool/po2lmo/src/po2lmo.c


+ 0 - 0
package/luci-compat/tool/po2lmo/src/template_lmo.c → package/tool/po2lmo/src/template_lmo.c


+ 0 - 0
package/luci-compat/tool/po2lmo/src/template_lmo.h → package/tool/po2lmo/src/template_lmo.h