add luci-app-openclash

This commit is contained in:
CN_SZTL 2019-07-19 18:18:45 +08:00
parent bc128f872e
commit a018e8f622
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
62 changed files with 5621 additions and 0 deletions

View File

@ -0,0 +1,45 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openclash
PKG_VERSION=0.30.0
PKG_RELEASE:=1
PKG_MAINTAINER:=frainzy1477&vernesong <https://github.com/vernesong/OpenClash>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
CATEGORY:=Utilities
SUBMENU:=Luci
TITLE:=LuCI support for clash
PKGARCH:=all
DEPENDS:=+coreutils-nohup +bash +wget
endef
define Package/$(PKG_NAME)/description
A LuCI support for clash
endef
define Package/$(PKG_NAME)/postinst
endef
define Build/Prepare
$(foreach po,$(wildcard ${CURDIR}/i18n/zh-cn/*.po), \
po2lmo $(po) $(PKG_BUILD_DIR)/$(patsubst %.po,%.lmo,$(notdir $(po)));)
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
$(INSTALL_DATA) $(PKG_BUILD_DIR)/*.*.lmo $(1)/usr/lib/lua/luci/i18n/
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1,13 @@
config openclash 'config'
option proxy_port '7892'
option enable '0'
option en_mode '0'
option auto_update '0'
option auto_update_time '0'
option cn_port '9090'
option dashboard_password '123456'
option rule_source '0'
option enable_custom_dns '0'
option enable_custom_clash_rules '0'
option other_rule_auto_update '0'

View File

@ -0,0 +1,13 @@
##- DOMAIN-SUFFIX,google.com,Proxy 匹配域名后缀(交由Proxy代理服务器组)
##- DOMAIN-KEYWORD,google,Proxy 匹配域名关键字(交由Proxy代理服务器组)
##- DOMAIN,google.com,Proxy 匹配域名(交由Proxy代理服务器组)
##- DOMAIN-SUFFIX,ad.com,REJECT 匹配域名后缀(拒绝)
##- IP-CIDR,127.0.0.0/8,DIRECT 匹配数据目标IP(直连)
##- SRC-IP-CIDR,192.168.1.201/32,DIRECT 匹配数据发起IP(直连)
##- DST-PORT,80,DIRECT 匹配数据目标端口(直连)
##- SRC-PORT,7777,DIRECT 匹配数据源端口(直连)
##排序在上的规则优先生效,如添加(去除#号):
##- SRC-IP-CIDR,192.168.1.201/32,Rule
##- SRC-IP-CIDR,192.168.1.0/24,DIRECT
##此时IP为192.168.1.201的客户端流量走代理(策略),其余客户端不走代理

View File

@ -0,0 +1,326 @@
#!/bin/sh /etc/rc.common
# Copyright (c) 2011-2015 OpenWrt.org
START=99
STOP=15
CLASH="/etc/openclash/clash"
CLASH_CONFIG="/etc/openclash"
CRON_FILE="/etc/crontabs/root"
CONFIG_FILE="/etc/openclash/config.yml"
LOG_FILE="/tmp/openclash.log"
START_LOG="/tmp/openclash_start.log"
BACKPACK_FILE="/etc/openclash/config.bak"
CHANGE_FILE="/tmp/yml_change.yml"
RULE_FILE="/tmp/yml_rules.yml"
DNS_FILE="/tmp/yml_dns.yml"
add_cron()
{
[ -z "`grep "openclash.log" "$CRON_FILE"`" ] && {
echo '0 0 * * 0 echo "" > /tmp/openclash.log' >> $CRON_FILE
}
[ -z "`grep "openclash.sh" "$CRON_FILE"`" ] && {
[ "$(uci get openclash.config.auto_update 2>/dev/null)" -eq 1 ] && echo "0 $(uci get openclash.config.auto_update_time 2>/dev/null) * * * /usr/share/openclash/openclash.sh" >> $CRON_FILE
}
[ -z "`grep "openclash_rule.sh" "$CRON_FILE"`" ] && {
[ "$(uci get openclash.config.other_rule_auto_update 2>/dev/null)" -eq 1 ] && echo "0 $(uci get openclash.config.other_rule_update_day_time 2>/dev/null) * * $(uci get openclash.config.other_rule_update_week_time 2>/dev/null) /usr/share/openclash/openclash_rule.sh" >> $CRON_FILE
}
crontab $CRON_FILE
nohup /usr/share/openclash/openclash_watchdog.sh &
}
del_cron()
{
sed -i '/openclash.sh/d' $CRON_FILE 2>/dev/null
sed -i '/openclash_rule.sh/d' $CRON_FILE 2>/dev/null
/etc/init.d/cron restart
}
yml_check()
{
if [ ! -f "$4" ]; then
cp "$3" "$4"
fi
if [ "$(grep -c '^##Custom DNS##' "$CONFIG_FILE")" -gt 0 ] && [ "$2" = 0 ]; then
cp "$4" "$3"
fi
if [ "$1" = 0 ]; then
if [ -f "$4" ]; then
[ ! -z "`grep "OpenClash-General" "$CONFIG_FILE"`" ] && {
cp "$4" "$3"
}
else
[ -z "`grep "OpenClash-General" "$CONFIG_FILE"`" ] && {
cp "$3" "$4"
}
fi
fi
sed -i '/OpenClash-General/d' "$3" 2>/dev/null
sed -i '/^Proxy:/i\#===================== OpenClash-General-Settings =====================#' "$3" 2>/dev/null
}
yml_dns_check()
{
if [ "$1" -eq 53 ]; then
sed -i "/^ listen:/c\ listen: 0.0.0.0:7874" "$2"
dns_port=7874
fi
[ -z "`grep "^dns:" $CONFIG_FILE`" ] && {
sed -i -e "/^Proxy:$/i\dns:"\
-e "/^dns:/a\ enable: true"\
-e "/enable: true/a\ listen: 0.0.0.0:${1}"\
-e "/listen:/a\ enhanced-mode: redir-host"\
-e "/enhanced-mode:/a\ nameserver:"\
-e "/nameserver:/a\ - 114.114.114.114"\
-e "/114.114.114.114/a\ - 119.29.29.29"\
-e "/119.29.29.29/a\ - 223.5.5.5"\
-e "/223.5.5.5/a\ fallback:"\
-e "/fallback:/a\ - tls://dns.rubyfish.cn:853"\
-e "/dns.rubyfish.cn:853/a\- tcp://1.1.1.1:53"\
-e "/1.1.1.1:53/a\ - tcp://208.67.222.222:443"\
-e "/208.67.222.222:443/a\ - tls://dns.google" "$2"
}
}
yml_cut()
{
cp "$5" "$2"
if [ "$1" = 0 ]; then
sed -i '/^Rule:$/,$d' "$2"
sed -n '/^Rule:$/,$p' "$5" >"$3"
else
sed -i '/^Rule:$/,$d' "$2"
sed -n '/nameserver:$/,$p' "$2" >"$4"
sed -i '/nameserver:$/,$d' "$2"
sed -n '/^Rule:$/,$p' "$5" >"$3"
fi
}
yml_dns_get()
{
local section="$1"
local dns_type=""
local dns_address=""
config_get "port" "$section" "port" ""
config_get "type" "$section" "type" ""
config_get "ip" "$section" "ip" ""
config_get "group" "$section" "group" ""
if [ -z "$ip" ]; then
return
fi
if [ "$type" = "tcp" ]; then
dns_type="- tcp://"
elif [ "$type" = "tls" ]; then
dns_type="- tls://"
elif [ "$type" = "udp" ]; then
dns_type="- "
fi
if [ ! -z "$port" ] && [ ! -z "$ip" ]; then
dns_address="$ip:$port"
elif [ -z "$port" ] && [ ! -z "$ip" ]; then
dns_address="$ip"
else
return
fi
if [ ! -z "$group" ]; then
if [ "$group" = "nameserver" ]; then
echo " $dns_type$dns_address" >>/etc/openclash/config.namedns
else
grep "fallback:$" /etc/openclash/config.falldns 1>/dev/null
if [ "$?" -ne "0" ]; then
echo " fallback:" >/etc/openclash/config.falldns
fi
echo " $dns_type$dns_address" >>/etc/openclash/config.falldns
fi
else
return
fi
}
yml_dns_custom()
{
[ "$1" = 1 ] && {
echo " nameserver:" >/etc/openclash/config.namedns
config_load "openclash"
config_foreach yml_dns_get "dns_servers"
sed -i '/nameserver:$/i\##Custom DNS##' "$2" 2>/dev/null
sed -i '/OpenClash-General-Settings/i\Custom DNS End' "$2" 2>/dev/null
sed -i '/nameserver:$/,/^Custom DNS End$/d' "$2" 2>/dev/null
sed -i '/^##Custom DNS##$/r/etc/openclash/config.falldns' "$2" 2>/dev/null
sed -i '/^##Custom DNS##$/r/etc/openclash/config.namedns' "$2" 2>/dev/null
rm -rf /etc/openclash/config.namedns 2>/dev/null
rm -rf /etc/openclash/config.falldns 2>/dev/null
}
}
start()
{
echo "OpenClash 开始启动..." >$START_LOG
enable=$(uci get openclash.config.enable 2>/dev/null)
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
if [ ! -f $CONFIG_FILE ] && [ -f $BACKPACK_FILE ]; then
cp $BACKPACK_FILE $CONFIG_FILE
fi
if [ "$enable" -eq 1 ] && [ -f $CONFIG_FILE ]; then
echo "第一步: 获取配置中..." >$START_LOG
en_mode=$(uci get openclash.config.en_mode 2>/dev/null)
enable_custom_dns=$(uci get openclash.config.enable_custom_dns 2>/dev/null)
rule_source=$(uci get openclash.config.rule_source 2>/dev/null)
enable_custom_clash_rules=$(uci get openclash.config.enable_custom_clash_rules 2>/dev/null)
da_password=$(uci get openclash.config.dashboard_password 2>/dev/null)
cn_port=$(uci get openclash.config.cn_port 2>/dev/null)
proxy_port=$(uci get openclash.config.proxy_port 2>/dev/null)
echo "第二步: 配置文件检查..." >$START_LOG
yml_check "$en_mode" "$enable_custom_dns" "$CONFIG_FILE" "$BACKPACK_FILE"
current_mode=$(grep "enhanced-mode:" $CONFIG_FILE |awk -F ' ' '{print $2}' 2>/dev/null)
dns_port=`grep listen: $CONFIG_FILE |awk -F ':' '{print $3}' |tr -cd "[0-9]"`
yml_dns_check "$dns_port" "$CONFIG_FILE"
echo "第三步: 修改配置文件..." >$START_LOG
yml_cut "$enable_custom_dns" "$CHANGE_FILE" "$RULE_FILE" "$DNS_FILE" "$CONFIG_FILE"
yml_dns_custom "$enable_custom_dns" "$DNS_FILE"
sh /usr/share/openclash/yml_change.sh "$LOGTIME" "$en_mode" "$enable_custom_dns" "$da_password" "$cn_port" "$proxy_port" "$current_mode" "$CHANGE_FILE" &
sh /usr/share/openclash/yml_rules_change.sh "$LOGTIME" "$rule_source" "$enable_custom_clash_rules" "$RULE_FILE" &
wait
cat $CHANGE_FILE $DNS_FILE $RULE_FILE > $CONFIG_FILE 2>/dev/null
rm -rf /tmp/yml_* 2>/dev/null
echo "第四步: 配置文件完整性检查..." >$START_LOG
grep "^Proxy Group:$" $CONFIG_FILE 1>/dev/null && grep "^Rule:$" $CONFIG_FILE 1>/dev/null && grep "^- GEOIP" $CONFIG_FILE 1>/dev/null && grep "nameserver:$" $CONFIG_FILE 1>/dev/null
if [ "$?" -ne "0" ]; then
echo "错误: 配置文件完整性检查不通过,已自动还原配置文件,请对照模板格式检查修改配置文件..." >$START_LOG
cp $BACKPACK_FILE $CONFIG_FILE
echo "${LOGTIME} OpenClash Config Error,Please Check And Try Again" >>$LOG_FILE
sleep 20
echo "" >$START_LOG
elif [ ! -z "`sed -n '/nameserver:/{n;p}' $CONFIG_FILE |grep 'fallback:'`" ]; then
echo "错误: 配置文件DNS选项下的Nameserver必须设置服务器已自动还原配置文件请修改好后再重新启动..." >$START_LOG
echo "${LOGTIME} Nameserver Must Be Set, Please Change Your Configrations In Config.yml" >>$LOG_FILE
cp $BACKPACK_FILE $CONFIG_FILE
sleep 20
echo "" >$START_LOG
else
echo "第五步: 启动 Clash 主程序..." >$START_LOG
nohup $CLASH -d "$CLASH_CONFIG" > $LOG_FILE 2>&1 &
echo "第六步: 设置 OpenClash 防火墙规则..." >$START_LOG
ln -s /usr/share/openclash/web /www/openclash 2>/dev/null
uci set firewall.@defaults[0].flow_offloading=1
uci commit firewall
/etc/init.d/firewall restart >/dev/null 2>&1
lan_ip=$(uci get network.lan.ipaddr)
iptables -t nat -N openclash
iptables -t nat -N openclash_dns
iptables -t nat -A openclash -d 0.0.0.0/8 -j RETURN
iptables -t nat -A openclash -d 10.0.0.0/8 -j RETURN
iptables -t nat -A openclash -d 127.0.0.0/8 -j RETURN
iptables -t nat -A openclash -d 169.254.0.0/16 -j RETURN
iptables -t nat -A openclash -d 172.16.0.0/12 -j RETURN
iptables -t nat -A openclash -d 192.168.0.0/16 -j RETURN
iptables -t nat -A openclash -d 224.0.0.0/4 -j RETURN
iptables -t nat -A openclash -d 240.0.0.0/4 -j RETURN
iptables -t nat -A openclash_dns -p udp -j REDIRECT --to-ports "$dns_port"
iptables -t nat -A openclash_dns -p tcp -j REDIRECT --to-ports "$dns_port"
iptables -t nat -A openclash -p tcp -j REDIRECT --to-ports "$proxy_port"
iptables -t nat -I PREROUTING 1 -p tcp --dport 53 -j openclash_dns
iptables -t nat -I PREROUTING 1 -p udp --dport 53 -j openclash_dns
iptables -t nat -A PREROUTING -p tcp -j openclash
echo "第七步: 重启 Dnsmasq 程序..." >$START_LOG
/etc/init.d/dnsmasq restart >/dev/null 2>&1
if pidof clash >/dev/null; then
echo "第八步: 添加 OpenClash 计划任务,启动进程守护程序..." >$START_LOG
add_cron
echo "OpenClash 启动成功,请等待服务器上线!" >$START_LOG
echo "${LOGTIME} OpenClash Start Successfully" >> $LOG_FILE
sleep 5
echo "" >$START_LOG
else
echo "错误: OpenClash 启动失败,请到日志页面查看详细错误信息!" >$START_LOG
echo "${LOGTIME} OpenClash Can Not Start, Please Check The Error Info And Try Again" >> $LOG_FILE
sleep 20
echo "" >$START_LOG
stop
fi
fi
else
echo "错误: OpenClash 缺少配置文件,请上传或更新配置文件!" >$START_LOG
echo "${LOGTIME} config.yml Missing Or Clash Not Enable" >> $LOG_FILE
sleep 5
echo "" >$START_LOG
fi
}
stop()
{
echo "OpenClash 开始关闭..." >$START_LOG
echo "第一步: 删除 OpenClash 防火墙规则..." >$START_LOG
iptables -t nat -F openclash >/dev/null 2>&1
iptables -t nat -F openclash_dns >/dev/null 2>&1
nat_clashs=$(iptables -nvL PREROUTING -t nat | sed 1,2d | sed -n '/openclash/=' | sort -r)
for nat_clash in $nat_clashs; do
iptables -t nat -D PREROUTING "$nat_clash" >/dev/null 2>&1
done
iptables -t nat -X openclash >/dev/null 2>&1
iptables -t nat -X openclash_dns >/dev/null 2>&1
uci set firewall.@defaults[0].flow_offloading=1
uci commit firewall
/etc/init.d/firewall restart >/dev/null 2>&1
echo "第二步: 关闭 OpenClash 守护程序..." >$START_LOG
kill -9 "$(ps |grep openclash_watchdog.sh |grep -v grep |awk '{print $1}')" >/dev/null 2>&1
echo "第三步: 关闭 Clash 主程序..." >$START_LOG
kill -9 "$(pidof clash|sed 's/$//g')" 2>/dev/null
echo "第四步: 删除 OpenClash 计划任务..." >$START_LOG
del_cron
echo "第五步: 重启 Dnsmasq 程序..." >$START_LOG
/etc/init.d/dnsmasq restart >/dev/null 2>&1
echo "第六步: 删除 OpenClash 残留文件..." >$START_LOG
rule_source=$(uci get openclash.config.rule_source 2>/dev/null)
if [ "$rule_source" != 0 ]; then
rm -rf /tmp/clash_rule.yml >/dev/null 2>&1
rm -rf /tmp/Proxy_Group >/dev/null 2>&1
fi
enable=$(uci get openclash.config.enable 2>/dev/null)
if [ "$enable" -eq 0 ]; then
rm -rf $LOG_FILE
rm -rf /www/openclash 2> /dev/null
rm -rf /tmp/openclash_last_version 2> /dev/null
sed -i '/openclash.log/d' $CRON_FILE
echo "OpenClash 关闭成功!" >$START_LOG
rm -rf $START_LOG
else
echo "OpenClash 关闭成功!" >$START_LOG
fi
echo "CLASH STOP"
}
restart()
{
stop
start
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
Rule:
##source:ConnersHua_return
# (Video)
# AcFun
# USER-AGENT,AcFun*,PROXY
- DOMAIN-SUFFIX,acfun.cn,PROXY
- DOMAIN-SUFFIX,acfun.com,PROXY
- DOMAIN-SUFFIX,aixifan.com,PROXY
# > bilibili
# USER-AGENT,bili-universal,PROXY
# USER-AGENT,Bilibili*,PROXY
- DOMAIN-SUFFIX,acgvideo.com,PROXY
- DOMAIN-SUFFIX,bilibili.com,PROXY
- DOMAIN-SUFFIX,hdslb.com,PROXY
# > HunanTV
# USER-AGENT,MGTV*,PROXY
- DOMAIN-SUFFIX,hitv.com,PROXY
- DOMAIN-SUFFIX,hunantv.com,PROXY
- DOMAIN-SUFFIX,mgtv.com,PROXY
# > Migu
# USER-AGENT,MiguVideo*,PROXY
# USER-AGENT,%E5%92%AA%E5%92%95%E8%A7%86%E9%A2%91,PROXY
- DOMAIN-SUFFIX,cmvideo.cn,PROXY
- DOMAIN-SUFFIX,migu.cn,PROXY
- DOMAIN-SUFFIX,miguvideo.com,PROXY
# > iQiyi
# USER-AGENT,iQiYiPhoneVideo,PROXY
# USER-AGENT,PPStream,PROXY
# USER-AGENT,QIYIVideo,PROXY
# USER-AGENT,QYPlayer,PROXY
- DOMAIN-SUFFIX,iqiyi.com,PROXY
- DOMAIN-SUFFIX,iqiyipic.com,PROXY
- DOMAIN-SUFFIX,qy.net,PROXY
# > Sohu
- DOMAIN-SUFFIX,sohu.com,PROXY
- DOMAIN-SUFFIX,sohu.com.cn,PROXY
- DOMAIN-SUFFIX,itc.cn,PROXY
- DOMAIN-SUFFIX,v-56.com,PROXY
# > Tencent
# USER-AGENT,live4iphone*,PROXY
# USER-AGENT,qqlive4iphone*,PROXY
# USER-AGENT,TencentMidasConnect*,PROXY
- DOMAIN-SUFFIX,video.qq.com,PROXY
# > Youku
# USER-AGENT,Youku*,PROXY
# USER-AGENT,%E4%BC%98%E9%85%B7*,PROXY
- DOMAIN-SUFFIX,soku.com,PROXY
- DOMAIN-SUFFIX,youku.com,PROXY
- DOMAIN-SUFFIX,ykimg.com,PROXY
# (Music)
# > Alibaba
# USER-AGENT,walkman*,PROXY
# USER-AGENT,xiami*,PROXY
- DOMAIN-SUFFIX,xiami.com,PROXY
- DOMAIN-SUFFIX,xiami.net,PROXY
# > Netease
# USER-AGENT,NeteaseMusic*,PROXY
# USER-AGENT,%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90*,PROXY
- DOMAIN-SUFFIX,music.126.net,PROXY
- DOMAIN-SUFFIX,music.163.com,PROXY
# > Tencent
# USER-AGENT,MOO%E9%9F%B3%E4%B9%90*,PROXY
# USER-AGENT,QQ%E9%9F%B3%E4%B9%90,PROXY
- DOMAIN-SUFFIX,qqmusic.qq.com,PROXY
- DOMAIN-SUFFIX,y.qq.com,PROXY
- DOMAIN,aqqmusic.tc.qq.com,PROXY
# Kugou and Kuwo
- DOMAIN-SUFFIX,kugou.com,PROXY
# USER-AGENT,%E9%85%B7%E6%88%91%E9%9F%B3%E4%B9%90*,PROXY
- DOMAIN-SUFFIX,kuwo.cn,PROXY
- DOMAIN-SUFFIX,koowo.com,PROXY
# > Baidu
# USER-AGENT,baiduyinyue,PROXY
- DOMAIN-SUFFIX,qianqian.com,PROXY
- GEOIP,CN,PROXY
- MATCH,DIRECT

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,128 @@
module("luci.controller.openclash", package.seeall)
function index()
if not nixio.fs.access("/etc/config/openclash") then
return
end
entry({"admin", "services", "openclash"},alias("admin", "services", "openclash", "client"), _("OpenClash"), 50).dependent = true
entry({"admin", "services", "openclash", "client"},cbi("openclash/client"),_("Clash Global State"), 20).leaf = true
entry({"admin", "services", "openclash", "status"},call("action_status")).leaf=true
entry({"admin", "services", "openclash", "state"},call("action_state")).leaf=true
entry({"admin", "services", "openclash", "startlog"},call("action_start")).leaf=true
entry({"admin", "services", "openclash", "currentversion"},call("action_currentversion"))
entry({"admin", "services", "openclash", "lastversion"},call("action_lastversion"))
entry({"admin", "services", "openclash", "config"},cbi("openclash/config"),_("Server Config"), 30).leaf = true
entry({"admin", "services", "openclash", "settings"},cbi("openclash/settings"),_("Clash Settings"), 40).leaf = true
entry({"admin", "services", "openclash", "update"},cbi("openclash/update"),_("Update Setting"), 50).leaf = true
entry({"admin", "services", "openclash", "rule"},cbi("openclash/rule"),_("Rules Setting"), 60).leaf = true
entry({"admin", "services", "openclash", "log"},cbi("openclash/log"),_("Logs"), 70).leaf = true
end
local function is_running()
return luci.sys.call("pidof clash >/dev/null") == 0
end
local function is_web()
return luci.sys.call("pidof clash >/dev/null") == 0
end
local function is_watchdog()
return luci.sys.exec("ps |grep openclash_watchdog.sh |grep -v grep 2>/dev/null")
end
local function config_check()
return luci.sys.call("grep '^Proxy Group:$' /etc/openclash/config.yml 1>/dev/null && grep '^Rule:$' /etc/openclash/config.yml 1>/dev/null && grep '^- GEOIP' /etc/openclash/config.yml 1>/dev/null && grep ' nameserver:$' /etc/openclash/config.yml 1>/dev/null") == 0
end
local function cn_port()
return luci.sys.exec("uci get openclash.config.cn_port 2>/dev/null")
end
local function config()
return luci.sys.exec("ls -l --full-time /etc/openclash/config.bak|awk '{print $6,$7;}' 2>/dev/null")
end
local function ipdb()
return luci.sys.exec("ls -l --full-time /etc/openclash/Country.mmdb|awk '{print $6,$7;}' 2>/dev/null")
end
local function lhie1()
return luci.sys.exec("ls -l --full-time /etc/openclash/lhie1.yml|awk '{print $6,$7;}' 2>/dev/null")
end
local function ConnersHua()
return luci.sys.exec("ls -l --full-time /etc/openclash/ConnersHua.yml|awk '{print $6,$7;}' 2>/dev/null")
end
local function ConnersHua_return()
return luci.sys.exec("ls -l --full-time /etc/openclash/ConnersHua_return.yml|awk '{print $6,$7;}' 2>/dev/null")
end
local function daip()
return luci.sys.exec("uci get network.lan.ipaddr")
end
local function dase()
return luci.sys.exec("uci get openclash.config.dashboard_password 2>/dev/null")
end
local function check_lastversion()
return luci.sys.exec("sh /usr/share/openclash/openclash_version.sh && sed -n '/^data:/,$p' /tmp/openclash_last_version 2>/dev/null")
end
local function check_currentversion()
return luci.sys.exec("sed -n '/^data:/,$p' /etc/openclash/openclash_version 2>/dev/null")
end
local function startlog()
return luci.sys.exec("sed -n '$p' /tmp/openclash_start.log 2>/dev/null")
end
function action_status()
luci.http.prepare_content("application/json")
luci.http.write_json({
clash = is_running(),
watchdog = is_watchdog(),
daip = daip(),
dase = dase(),
web = is_web(),
cn_port = cn_port();
})
end
function action_state()
luci.http.prepare_content("application/json")
luci.http.write_json({
config_check = config_check(),
config = config(),
lhie1 = lhie1(),
ConnersHua = ConnersHua(),
ConnersHua_return = ConnersHua_return(),
ipdb = ipdb();
})
end
function action_lastversion()
luci.http.prepare_content("application/json")
luci.http.write_json({
lastversion = check_lastversion();
})
end
function action_currentversion()
luci.http.prepare_content("application/json")
luci.http.write_json({
currentversion = check_currentversion();
})
end
function action_start()
luci.http.prepare_content("application/json")
luci.http.write_json({
startlog = startlog();
})
end

View File

@ -0,0 +1,46 @@
local NXFS = require "nixio.fs"
local SYS = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local UTIL = require "luci.util"
local t = {
{enable, disable}
}
a = SimpleForm("apply")
a.reset = false
a.submit = false
s = a:section(Table, t)
o = s:option(Button, "enable")
o.inputtitle = translate("Enable Clash")
o.inputstyle = "apply"
o.write = function()
os.execute("uci set openclash.config.enable=1 && uci commit openclash && /etc/init.d/openclash restart >/dev/null 2>&1 &")
HTTP.redirect(DISP.build_url("admin", "services", "openclash"))
end
o = s:option(Button, "disable")
o.inputtitle = translate("Disable Clash")
o.inputstyle = "reset"
o.write = function()
os.execute("uci set openclash.config.enable=0 && uci commit openclash && /etc/init.d/openclash stop >/dev/null 2>&1 &")
end
m = Map("openclash")
m.title = translate("OpenClash")
m.description = translate("A Clash Client For OpenWrt")
m.pageaction = false
m:section(SimpleSection).template = "openclash/status"
m:section(SimpleSection).template = "openclash/state"
m:section(SimpleSection).template = "openclash/myip"
d = Map("openclash")
d.title = translate("Technical Support")
d.pageaction = false
d:section(SimpleSection).template = "openclash/developer"
return m, a, d

View File

@ -0,0 +1,74 @@
local NXFS = require "nixio.fs"
local SYS = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local UTIL = require "luci.util"
local http = luci.http
ful = SimpleForm("upload", translate("Server Configuration"), nil)
ful.reset = false
ful.submit = false
sul =ful:section(SimpleSection, "", translate(""))
o = sul:option(FileUpload, "")
o.title = translate("Upload Clash Configuration")
o.template = "openclash/clash_upload"
o.description = translate("NB: Rename your config file to config.yml before upload. file will save to /etc/openclash")
um = sul:option(DummyValue, "", nil)
um.template = "openclash/clash_dvalue"
local dir, fd
dir = "/etc/openclash/"
http.setfilehandler(
function(meta, chunk, eof)
if not fd then
if not meta then return end
if meta and chunk then fd = nixio.open(dir .. meta.file, "w") end
if not fd then
um.value = translate("upload file error.")
return
end
end
if chunk and fd then
fd:write(chunk)
end
if eof and fd then
fd:close()
fd = nil
um.value = translate("File saved to") .. ' "/etc/openclash/' .. meta.file .. '"'
SYS.exec("cp /etc/openclash/config.yml /etc/openclash/config.bak")
end
end
)
if luci.http.formvalue("upload") then
local f = luci.http.formvalue("ulfile")
if #f <= 0 then
um.value = translate("No specify upload file.")
end
end
m = Map("openclash")
s = m:section(TypedSection, "openclash")
s.anonymous = true
s.addremove=false
local conf = "/etc/openclash/config.yml"
sev = s:option(TextValue, "conf")
sev.readonly=true
sev.description = translate("Changes to config file must be made from source")
sev.rows = 20
sev.wrap = "off"
sev.cfgvalue = function(self, section)
return NXFS.readfile(conf) or ""
end
sev.write = function(self, section, value)
end
return ful , m

View File

@ -0,0 +1,28 @@
--
local NXFS = require "nixio.fs"
local SYS = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local UTIL = require "luci.util"
m = Map("openclash", translate("Server logs"))
s = m:section(TypedSection, "openclash")
m.pageaction = false
s.anonymous = true
s.addremove=false
local clog = "/tmp/openclash.log"
log = s:option(TextValue, "clog")
log.readonly=true
log.description = translate("")
log.rows = 29
log.wrap = "off"
log.cfgvalue = function(self, section)
return NXFS.readfile(clog) or ""
end
log.write = function(self, section, value)
NXFS.writefile(clog, value:gsub("\r\n", "\n"))
end
return m

View File

@ -0,0 +1,123 @@
--
local NXFS = require "nixio.fs"
local SYS = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local UTIL = require "luci.util"
m = Map("openclash", translate("Rules Setting"))
s = m:section(TypedSection, "openclash")
s.anonymous = true
o = s:option(ListValue, "enable_custom_clash_rules", translate("Custom Clash Rules"))
o.description = translate("Use Custom Rules")
o:value("0", translate("Disable Custom Clash Rules"))
o:value("1", translate("Enable Custom Clash Rules"))
o.default = 0
o = s:option(ListValue, "rule_source", translate("Enable Other Rules"))
o.description = translate("Use Other Rules")
o:value("0", translate("Disable Other Rules"))
o:value("lhie1", translate("lhie1 Rules"))
o:value("ConnersHua", translate("ConnersHua Rules"))
o:value("ConnersHua_return", translate("ConnersHua Return Rules"))
os.execute("awk '/Proxy Group:/,/Rule:/{print}' /etc/openclash/config.yml |grep ^- |grep name: |sed 's/,.*//' |awk -F 'name: ' '{print $2}' |sed 's/\"//g' >/tmp/Proxy_Group 2>&1")
os.execute("echo 'DIRECT' >>/tmp/Proxy_Group")
os.execute("echo 'REJECT' >>/tmp/Proxy_Group")
file = io.open("/tmp/Proxy_Group", "r");
o = s:option(ListValue, "GlobalTV", translate("GlobalTV"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
for l in file:lines() do
o:value(l)
end
file:seek("set")
o = s:option(ListValue, "AsianTV", translate("AsianTV"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
for l in file:lines() do
o:value(l)
end
file:seek("set")
o = s:option(ListValue, "Proxy", translate("Proxy"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
o:depends("rule_source", "ConnersHua_return")
for l in file:lines() do
o:value(l)
end
file:seek("set")
o = s:option(ListValue, "Apple", translate("Apple"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
for l in file:lines() do
o:value(l)
end
file:seek("set")
o = s:option(ListValue, "AdBlock", translate("AdBlock"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
for l in file:lines() do
o:value(l)
end
file:seek("set")
o = s:option(ListValue, "Domestic", translate("Domestic"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
for l in file:lines() do
o:value(l)
end
file:seek("set")
o = s:option(ListValue, "Others", translate("Others"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua")
o:depends("rule_source", "ConnersHua_return")
o.description = translate("Choose Proxy Group, Base On Your Servers Group in config.yml")
for l in file:lines() do
o:value(l)
end
file:close()
custom_rules = s:option(Value, "custom_rules", translate("Custom Clash Rules Here"), translate("For More Go Github:https://github.com/Dreamacro/clash"))
custom_rules.template = "cbi/tvalue"
custom_rules.rows = 20
custom_rules.wrap = "off"
custom_rules:depends("enable_custom_clash_rules", 1)
function custom_rules.cfgvalue(self, section)
return NXFS.readfile("/etc/config/openclash_custom_rules.list") or ""
end
function custom_rules.write(self, section, value)
if value then
value = value:gsub("\r\n", "\n")
NXFS.writefile("/etc/config/openclash_custom_rules.list", value)
end
end
local t = {
{Commit, Apply}
}
a = SimpleForm("apply")
a.reset = false
a.submit = false
s = a:section(Table, t)
o = s:option(Button, "Commit")
o.inputtitle = translate("Commit Configurations")
o.inputstyle = "apply"
o.write = function()
os.execute("uci commit openclash")
end
o = s:option(Button, "Apply")
o.inputtitle = translate("Apply Configurations")
o.inputstyle = "apply"
o.write = function()
os.execute("uci commit openclash && /etc/init.d/openclash restart >/dev/null 2>&1 &")
HTTP.redirect(DISP.build_url("admin", "services", "openclash"))
end
return m , a

View File

@ -0,0 +1,107 @@
local NXFS = require "nixio.fs"
local SYS = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local UTIL = require "luci.util"
local t = {
{Commit, Apply}
}
a = SimpleForm("apply")
a.reset = false
a.submit = false
s = a:section(Table, t)
o = s:option(Button, "Commit")
o.inputtitle = translate("Commit Configurations")
o.inputstyle = "apply"
o.write = function()
os.execute("uci commit openclash")
end
o = s:option(Button, "Apply")
o.inputtitle = translate("Apply Configurations")
o.inputstyle = "apply"
o.write = function()
os.execute("uci commit openclash && /etc/init.d/openclash restart >/dev/null 2>&1 &")
HTTP.redirect(DISP.build_url("admin", "services", "openclash"))
end
m = Map("openclash", translate("Clash Settings"))
m.pageaction = false
s = m:section(TypedSection, "openclash")
s.anonymous = true
o = s:option(ListValue, "en_mode", translate("Select Mode"))
o.description = translate("Will to Take Over Your General Settings, Network Error Try Flush DNS Cache")
o:value("0", translate("Disable Mode Control"))
o:value("redir-host", translate("redir-host"))
o:value("fake-ip", translate("fake-ip"))
o.default = 0
o = s:option(ListValue, "enable_custom_dns", translate("Custom DNS Setting"))
o.description = translate("Set OpenClash Upstream DNS Resolve Server")
o:value("0", translate("Disable Custom DNS Setting"))
o:value("1", translate("Enable Custom DNS Setting"))
o.default = 0
o = s:option(Value, "proxy_port")
o.title = translate("Clash config redir-port")
o.default = 7892
o.datatype = "port"
o.rmempty = false
o.description = translate("Clash Redir Port, Please Make Sure Ports Available")
o = s:option(Value, "cn_port")
o.title = translate("Dashboard Port")
o.default = 9090
o.datatype = "port"
o.rmempty = false
o.description = translate("Dashboard hostname is Your Router IP. Dashboard:192.168.1.1/openclash")
o = s:option(Value, "dashboard_password")
o.title = translate("Dashboard Secret")
o.default = 123456
o.rmempty = false
o.description = translate("Set Dashboard Secret")
-- [[ Edit Server ]] --
s = m:section(TypedSection, "dns_servers", translate("Add Custom DNS Servers"))
s.anonymous = true
s.addremove = true
s.sortable = false
s.template = "cbi/tblsection"
s.rmempty = false
---- group
o = s:option(ListValue, "group", translate("DNS Server Group"))
o:value("nameserver", translate("Nameserver"))
o:value("fallback", translate("Fallback"))
o.default = "udp"
o.rempty = false
---- IP address
o = s:option(Value, "ip", translate("DNS Server IP"))
o.datatype = "or(host, string)"
o.rmempty = false
---- port
o = s:option(Value, "port", translate("DNS Server Port"))
o.placeholder = "53"
o.datatype = "port"
o.rempty = false
---- type
o = s:option(ListValue, "type", translate("DNS Server Type"))
o:value("udp", translate("udp"))
o:value("tcp", translate("tcp"))
o:value("tls", translate("tls"))
o.default = "udp"
o.rempty = false
return m, a

View File

@ -0,0 +1,97 @@
--
local NXFS = require "nixio.fs"
local SYS = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local UTIL = require "luci.util"
m = Map("openclash", translate("Update Setting"))
s = m:section(TypedSection, "openclash", translate("Subscription Update"))
s.anonymous = true
o = s:option(Flag, "auto_update", translate("Auto Update"))
o.description = translate("Auto Update Server subscription")
o.default=0
o.rmempty = false
o = s:option(ListValue, "auto_update_time", translate("Update time (every day)"))
for t = 0,23 do
o:value(t, t..":00")
end
o.default=0
o.rmempty = false
o = s:option(Value, "subscribe_url")
o.title = translate("Subcription Url")
o.description = translate("Server Subscription Address")
o.rmempty = true
o = s:option(Button,translate("Config File Update"))
o.title = translate("Update Subcription")
o.inputtitle = translate("Update Configuration")
o.inputstyle = "reload"
o.write = function()
SYS.call("uci commit openclash && sh /usr/share/openclash/openclash.sh >/dev/null 2>&1 &")
HTTP.redirect(DISP.build_url("admin", "services", "openclash"))
end
f = m:section(TypedSection, "openclash", translate("Other Rules Update(Only in Use)"))
f.anonymous = true
o = f:option(Flag, "other_rule_auto_update", translate("Auto Update"))
o.description = translate("Auto Update Other Rules")
o.default=0
o.rmempty = false
o = f:option(ListValue, "other_rule_update_week_time", translate("Update Time (Every Week)"))
o:value("1", translate("Every Monday"))
o:value("2", translate("Every Tuesday"))
o:value("3", translate("Every Wednesday"))
o:value("4", translate("Every Thursday"))
o:value("5", translate("Every Friday"))
o:value("6", translate("Every Saturday"))
o:value("7", translate("Every Sunday"))
o.default=1
o = f:option(ListValue, "other_rule_update_day_time", translate("Update time (every day)"))
for t = 0,23 do
o:value(t, t..":00")
end
o.default=0
o = f:option(Button,translate("Other Rules Update"))
o.title = translate("Update Other Rules")
o.inputtitle = translate("Start Update Other Rules")
o.inputstyle = "reload"
o.write = function()
SYS.call("uci commit openclash && sh /usr/share/openclash/openclash_rule.sh >/dev/null 2>&1 &")
HTTP.redirect(DISP.build_url("admin", "services", "openclash"))
end
u = m:section(TypedSection, "openclash", translate("GEOIP(By MaxMind) Update"))
u.anonymous = true
o = u:option(Button,translate("GEOIP Database Update"))
o.title = translate("Update GEOIP Database")
o.inputtitle = translate("Start Update GEOIP Database")
o.inputstyle = "reload"
o.write = function()
SYS.call("sh /usr/share/openclash/openclash_ipdb.sh >/dev/null 2>&1 &")
HTTP.redirect(DISP.build_url("admin", "services", "openclash"))
end
local t = {
{Commit}
}
a = SimpleForm("apply")
a.reset = false
a.submit = false
s = a:section(Table, t)
o = s:option(Button, "Commit")
o.inputtitle = translate("Commit Configurations")
o.inputstyle = "apply"
o.write = function()
os.execute("uci commit openclash")
end
return m , a

View File

@ -0,0 +1,8 @@
<%+cbi/valueheader%>
<span style="color: green">
<%
local val = self:cfgvalue(section) or self.default or ""
write(pcdata(val))
%>
</span>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,5 @@
<%+cbi/valueheader%>
<label class="cbi-value" style="display:inline-block;" for="ulfile"></label>
<input class="cbi-input-file" style="width: 400px" type="file" id="ulfile" name="ulfile" />
<input type="submit" class="cbi-button cbi-input-reload" name="upload" value="<%:Upload%>" />
<%+cbi/valuefooter%>

View File

@ -0,0 +1,56 @@
<fieldset class="cbi-section">
<table>
<tr><td width="25%"> Clash 主程序发布: </td><td width="25%" align="left" id="_binupdate"><%:Collecting data...%></td><td width="25%"> OpenClash 客户端发布: </td><td width="25%" align="left" id="_openupdate"><%:Collecting data...%></td></tr>
<tr><td width="25%"> Clash 主程序开发: Dreamacro </td><td width="25%" align="left" id="_Dreamacro"><%:Collecting data...%></td><td width="25%"> OpenClash 客户端开发: vernesong </td><td width="25%" align="left" id="_vernesong"><%:Collecting data...%></td></tr>
<tr><td width="25%"> Clash 客户端开发: frainzy1477 </td><td width="25%" align="left" id="_frainzy1477"><%:Collecting data...%></td><td width="25%"> MyIP: SukkaW </td><td width="25%" align="left" id="_SukkaW"><%:Collecting data...%></td></tr>
</table>
</fieldset>
<script type="text/javascript">//<![CDATA[
var Dreamacro = document.getElementById('_Dreamacro');
var vernesong = document.getElementById('_vernesong');
var frainzy1477 = document.getElementById('_frainzy1477');
var SukkaW = document.getElementById('_SukkaW');
var binupdate = document.getElementById('_binupdate');
var openupdate = document.getElementById('_openupdate');
var github = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAdCAMAAACKeiw+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRjUyRDgzQjhBRDcxMUU5ODM5RkRFNTEzOTgzREM4RiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRjUyRDgzQzhBRDcxMUU5ODM5RkRFNTEzOTgzREM4RiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkZGNTJEODM5OEFENzExRTk4MzlGREU1MTM5ODNEQzhGIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkZGNTJEODNBOEFENzExRTk4MzlGREU1MTM5ODNEQzhGIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+F8FkZAAAAddQTFRF////AAAAAQEBAgICcnJyAwMDVVBLZmZma2trFxcXDQ0NCwsLjY2NuLi4UFBQurq6e3t7NjY2PDw8m5ubSEhISkpKMDAw7OTe/vv5cXFxqKiovLy819fX1MvEtbW1hX56UEtG5t/YhX54/f39WVlZmpqaLyso4drU1s7HXFxcVVFNf39/WVJM0dLSoKCgk5OTfHx8ysrKLi4uvb29RkZG6+vrp52Vr6+v1MzGpZuU5d7Z8uvkvrSr8Orkw8PD1cvCWFNNqaKdx720PDczxLqxw7qzQUFBV1dXQDs3qZ+W8fHx0Ma9t62ksbGxubq7IiIhCAgInp6eDg4O5t7Xsami7u7u/fv5oaGhcHBwISIjbGxsJiYnqamp0snA/Pz8o6Oj8PDw1tbWwbivz8/PBwcHUE1L//38g3pzOzs7wcHBSEVFZWVlq6ur29LLn5+fpJuSfHZyIB8f5OTkyMjIYGBgJCIfPj4+ubCoMCwpPDg0FBQUtKqhQEBATEpH6ubi4tnRDg8Qs7Oz1s3F8uvm6enpHhsZvrSsVFBMioN8JiYmsaql+vby/fr3IiIiKioqZ2Bb9/PvhH13HhwaVlRR6eDZPz8/MzMzDQ0OEQ8OOzg0kImCnZSL////7wd0TgAAAJ10Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AMv8vEwAAAF8SURBVHjabNNlY4MwEAbgO2jn1rm7u7u7u7u7u7u7a3/scmSsDHYfmuR9gKbNAcbf2r30e+rqGJkvyDRlIE98yvG31ktUfFSEf+qxScmDqKkME6fiPzUmczZbiIKSBJF9fHF+ZlO9V42rgAIrdp1w0TdkycI84tENNisFAN8X3fRSwkHUeCxb2LHw/YpxIT0tAlTlQGkFY3+aJKp5m9KTawiQ9rKq5jIpDodmGkRvNbdsUn4ODzS4gKY6KT+DSRo8tBxHf8QCvBE7aLmB2Ar2iS20bMN5h9hZyxaU10EgDY0pag26oTwS1qQfGK9mJ+mIPsFNYrH7r06IUjwDxmjE9lcUlo9NWKnjutIKxh7EWTBHHDaY95P16gzWP8deTeedj2gDjmyVzO+dkpsi1J04i331IthZ3uZyTpMbZ483UxVisGJbST+6JbdiMaKjk23IAGd7rnOmRj6ktd5TyafK1+DejLVnG2dbhh85qpco7M66lnNMvVm6nH4LMAB50ohVhJZ1iwAAAABJRU5ErkJggg=="
var release = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAeCAMAAAAfOR5kAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDNTk0NDQzNThBRDgxMUU5OTgwMkVGQkFDRDdENTg1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDNTk0NDQzNjhBRDgxMUU5OTgwMkVGQkFDRDdENTg1MyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1OTQ0NDMzOEFEODExRTk5ODAyRUZCQUNEN0Q1ODUzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkM1OTQ0NDM0OEFEODExRTk5ODAyRUZCQUNEN0Q1ODUzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+JpJc6gAAAPZQTFRFAAAA////+Pj4/v7+/f39+/v7AQEB39/f/Pz88vLyKysr3d3dCgoKpqam6urq4eHh7e3t4+Pj5eXlycnJ9PT09fX1BQUF9/f3AgICy8vLDg4OYmJi2NjY2tra8PDwz8/PNDQ0Pz8/v7+/ra2tu7u7wcHBQkJCjo6Om5ubXFxcXV1dDAwMCQkJLS0trKyszMzMIyMjRUVF0tLSxMTEeXl5ZWVlJiYm+vr61NTUjY2NyMjIXl5ekZGR6OjoV1dXoqKiLi4uRkZGT09P7OzsdnZ2fn5+xsbGsbGx1tbW5+fnFhYW3t7e8fHxY2NjkJCQJSUlERER////3OeyVgAAAFJ0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////AK0tDHEAAAD0SURBVHjajNHXUsMwEAXQKxfshFSSEDoEEnrvvYeS0O7//wyWZEaOpQf2YUd7ZrRaSWBwtesxE+3ybEywAGDdC57mL1/mKmHrKym9hN8g40dlbKv8kfAhrKgk3LR0zE975/iGEEt2k5KPRTjiFRsuXkXRxfu4d/E3nl18i7qLp8ETWzsCDAYWy6fi2cEo7rQomeJuxuDkdUDNZNzY1Pi4HCpA+icLmt/T8o8nNI//jwtTimuGw71qdXhUUmN0o6je8BSLsvoSmdSWCz9t0sveZsX0joyeZ49snmrcqo1OEj9ILbZzA1L0gU+feSbXjs36V4ABANF7Sn61vZbYAAAAAElFTkSuQmCC"
Dreamacro.innerHTML = '<img src="'+github+'" alt="Dreamacro" width="30" onclick="return Dreamacro_rediret()">';
vernesong.innerHTML = '<img src="'+github+'" alt="vernesong" width="30" onclick="return vernesong_rediret()">';
frainzy1477.innerHTML = '<img src="'+github+'" alt="frainzy1477" width="30" onclick="return frainzy1477_rediret()">';
SukkaW.innerHTML = '<img src="'+github+'" alt="SukkaW" width="30" onclick="return SukkaW_rediret()">';
binupdate.innerHTML = '<img src="'+release+'" alt="binupdate" width="30" height="30" onclick="return binupdate_rediret()">';
openupdate.innerHTML = '<img src="'+release+'" alt="openupdate" width="30" height="30" onclick="return openupdate_rediret()">';
function Dreamacro_rediret()
{
url1='https://github.com/Dreamacro';
window.open(url1);
}
function vernesong_rediret()
{
url2='https://github.com/vernesong';
window.open(url2);
}
function frainzy1477_rediret()
{
url3='https://github.com/frainzy1477';
window.open(url3);
}
function SukkaW_rediret()
{
url4='https://github.com/SukkaW';
window.open(url4);
}
function binupdate_rediret()
{
url5='https://github.com/Dreamacro/clash/releases';
window.open(url5);
}
function openupdate_rediret()
{
url6='https://github.com/vernesong/OpenClash/releases';
window.open(url6);
}
//]]></script>

View File

@ -0,0 +1,28 @@
<fieldset class="cbi-section">
<table>
<tr><td align="left"><input type="button" class="cbi-button cbi-button-reload" value="<%:刷新%>" onclick="refreshFrame(200)"/></td></tr>
</table>
<table>
<tr><td><iframe id="myip" name="myip" src="https://ip.skk.moe/simple/" scrolling="no" onerror="return refreshFrame(5000)" onload="iFrameHeight()" frameborder="0" style="padding: 0px; width:100%; height:120%; float:left; margin:0; padding:0;"></iframe></td></tr>
</table>
</fieldset>
<script type="text/javascript">//<![CDATA[
function iFrameHeight() {
var ifm = document.getElementById("myip");
var subWeb = document.frames ? document.frames["myip"].document : ifm.contentDocument;
if (ifm != null && subWeb != null) {
ifm.height = subWeb.body.scrollHeight;
ifm.width = subWeb.body.scrollWidth;
}
}
function refreshFrame(delay)
{
setTimeout(function(){ document.getElementById('myip').src="https://ip.skk.moe/simple/"; }, delay);
}
//]]></script>

View File

@ -0,0 +1,28 @@
<fieldset class="cbi-section">
<table>
<tr><td width="100%" colspan="4" align="center"><b> 一些必要的状态显示,运行前请确保各项目显示正常,需要更新请到更新设置页面操作 </b></td></tr>
<tr><td width="25%"> OpenClash 配置文件状态 </td><td width="25%" align="left" id="_config_check"><%:Collecting data...%></td><td width="25%"> OpenClash 配置文件更新日期 </td><td width="25%"v align="left" id="_config"><%:Collecting data...%></td></tr>
<tr><td width="25%"> GEOIP(By MaxMind) 数据库日期 </td><td width="25%" align="left" id="_ipdb"><%:Collecting data...%></td><td width="25%"> lhie1 规则更新日期 </td><td width="25%" align="left" id="_lhie1"><%:Collecting data...%></td></tr>
<tr><td width="25%"> ConnersHua 规则更新日期 </td><td width="25%" align="left" id="_ConnersHua"><%:Collecting data...%></td><td width="25%"> ConnersHua 回国规则更新日期 </td><td width="25%" align="left" id="_ConnersHua_return"><%:Collecting data...%></td></tr>
</table>
</fieldset>
<script type="text/javascript">//<![CDATA[
var config = document.getElementById('_config');
var ipdb = document.getElementById('_ipdb');
var lhie1 = document.getElementById('_lhie1');
var ConnersHua = document.getElementById('_ConnersHua');
var ConnersHua_return = document.getElementById('_ConnersHua_return');
var config_check = document.getElementById('_config_check');
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "services", "openclash", "state")%>', null, function(x, status) {
if ( x && x.status == 200 ) {
config_check.innerHTML = status.config_check ? '<b><font color=green><%:Config Normal%></font></b>' : '<b><font color=red><%:Config Abnormal%></font></b>';
config.innerHTML = status.config ? "<b><font color=green>"+status.config+"</font></b>" : "<b><font color=red>"+"<%:File No Update%>"+"</font></b>";
ipdb.innerHTML = status.ipdb ? "<b><font color=green>"+status.ipdb+"</font></b>" : "<b><font color=red>"+"<%:File Not Exist%>"+"</font></b>";
lhie1.innerHTML = status.lhie1 ? "<b><font color=green>"+status.lhie1+"</font></b>" : "<b><font color=red>"+"<%:File Not Exist%>"+"</font></b>";
ConnersHua.innerHTML = status.ConnersHua ? "<b><font color=green>"+status.ConnersHua+"</font></b>" : "<b><font color=red>"+"<%:File Not Exist%>"+"</font></b>";
ConnersHua_return.innerHTML = status.ConnersHua_return ? "<b><font color=green>"+status.ConnersHua_return+"</font></b>" : "<b><font color=red>"+"<%:File Not Exist%>"+"</font></b>";
}
});
//]]></script>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Clash</title>
<link href="main.5b2a02b81ea75793d649.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="js/1.bundle.5b2a02b81ea75793d649.min.js"></script><script type="text/javascript" src="js/bundle.5b2a02b81ea75793d649.min.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
#!/bin/sh
START_LOG="/tmp/openclash_start.log"
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
echo "开始下载配置文件..." >$START_LOG
subscribe_url=$(uci get openclash.config.subscribe_url 2>/dev/null)
wget-ssl --no-check-certificate "$subscribe_url" -O /tmp/config.yml
if [ "$?" -eq "0" ]; then
echo "配置文件下载成功,检查是否有更新..." >$START_LOG
if [ -f /etc/openclash/config.yml ]; then
cmp -s /etc/openclash/config.bak /tmp/config.yml
if [ "$?" -ne "0" ]; then
echo "配置文件有更新,开始替换..." >$START_LOG
mv /tmp/config.yml /etc/openclash/config.yml 2>/dev/null\
&& cp /etc/openclash/config.yml /etc/openclash/config.bak\
&& echo "配置文件替换成功,开始启动 OpenClash ..." >$START_LOG\
&& /etc/init.d/openclash restart 2>/dev/null
echo "${LOGTIME} Config Update Successful" >>/tmp/openclash.log
else
echo "配置文件没有任何更新,停止继续操作..." >$START_LOG
rm -rf /tmp/config.yml
echo "${LOGTIME} Updated Config No Change, Do Nothing" >>/tmp/openclash.log
sleep 5
echo "" >$START_LOG
fi
else
echo "配置文件下载成功,本地没有配置文件,开始创建 ..." >$START_LOG
mv /tmp/config.yml /etc/openclash/config.yml 2>/dev/null\
&& cp /etc/openclash/config.yml /etc/openclash/config.bak\
&& echo "配置文件创建成功,开始启动 OpenClash ..." >$START_LOG\
&& /etc/init.d/openclash restart 2>/dev/null
echo "${LOGTIME} Config Update Successful" >>/tmp/openclash.log
fi
else
echo "配置文件下载失败,请检查网络或稍后再试!" >$START_LOG
echo "${LOGTIME} Config Update Error" >>/tmp/openclash.log
rm -rf /tmp/config.yml 2>/dev/null
sleep 10
echo "" >$START_LOG
fi

View File

@ -0,0 +1,35 @@
#!/bin/sh
START_LOG="/tmp/openclash_start.log"
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
echo "开始下载 GEOIP 数据库..." >$START_LOG
wget-ssl --no-check-certificate https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz -O /tmp/ipdb.tar.gz
if [ "$?" -eq "0" ]; then
echo "GEOIP 数据库下载成功,检查数据库版本是否更新..." >$START_LOG
tar zxvf /tmp/ipdb.tar.gz -C /tmp >/dev/null 2>&1\
&& rm -rf /tmp/ipdb.tar.gz >/dev/null 2>&1
cmp -s /tmp/GeoLite2-Country_*/GeoLite2-Country.mmdb /etc/openclash/Country.mmdb
if [ "$?" -ne "0" ]; then
echo "数据库版本有更新,开始替换数据库版本..." >$START_LOG
/etc/init.d/openclash stop\
&& mv /tmp/GeoLite2-Country_*/GeoLite2-Country.mmdb /etc/openclash/Country.mmdb >/dev/null 2>&1\
&& /etc/init.d/openclash start\
&& echo "删除下载缓存..." >$START_LOG\
&& rm -rf /tmp/GeoLite2-Country_* >/dev/null 2>&1
echo "GEOIP 数据库更新成功!" >$START_LOG
echo "${LOGTIME} GEOIP Database Update Successful" >>/tmp/openclash.log
sleep 10
echo "" >$START_LOG
else
echo "数据库版本没有更新,停止继续操作..." >$START_LOG
echo "${LOGTIME} Updated GEOIP Database No Change, Do Nothing" >>/tmp/openclash.log
rm -rf /tmp/GeoLite2-Country_* >/dev/null 2>&1
sleep 5
echo "" >$START_LOG
fi
else
echo "GEOIP 数据库下载失败,请检查网络或稍后再试!" >$START_LOG
rm -rf /tmp/ipdb.tar.gz >/dev/null 2>&1
echo "${LOGTIME} GEOIP Database Update Error" >>/tmp/openclash.log
sleep 10
echo "" >$START_LOG
fi

View File

@ -0,0 +1,41 @@
#!/bin/sh
START_LOG="/tmp/openclash_start.log"
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
echo "开始获取使用中的第三方规则名称..." >$START_LOG
rule_source=$(uci get openclash.config.rule_source 2>/dev/null)
echo "开始下载使用中的第三方规则..." >$START_LOG
if [ "$rule_source" = "lhie1" ]; then
wget-ssl --no-check-certificate https://raw.githubusercontent.com/lhie1/Rules/master/Clash/Rule.yml -O /tmp/rules.yml
elif [ "$rule_source" = "ConnersHua" ]; then
wget-ssl --no-check-certificate https://raw.githubusercontent.com/ConnersHua/Profiles/master/Clash/Global.yml -O /tmp/rules.yml
sed -i -n '/^Rule:$/,$p' /tmp/rules.yml
elif [ "$rule_source" = "ConnersHua_return" ]; then
wget-ssl --no-check-certificate https://raw.githubusercontent.com/ConnersHua/Profiles/master/Clash/China.yml -O /tmp/rules.yml
sed -i -n '/^Rule:$/,$p' /tmp/rules.yml
fi
if [ "$?" -eq "0" ] && [ "$rule_source" != 0 ]; then
echo "下载成功,开始预处理规则文件..." >$START_LOG
sed -i "/^Rule:$/a\##source:${rule_source}" /tmp/rules.yml >/dev/null 2>&1
echo "检查下载的规则文件是否有更新..." >$START_LOG
cmp -s /etc/openclash/"$rule_source".yml /tmp/rules.yml
if [ "$?" -ne "0" ]; then
echo "检测到下载的规则文件有更新,开始替换..." >$START_LOG
mv /tmp/rules.yml /etc/openclash/"$rule_source".yml >/dev/null 2>&1
sed -i '/^Rule:$/a\##updated' /etc/openclash/"$rule_source".yml >/dev/null 2>&1
echo "替换成功,重新加载 OpenClash 应用新规则..." >$START_LOG
/etc/init.d/openclash reload 2>/dev/null
echo "${LOGTIME} Other Rules Update Successful" >>/tmp/openclash.log
else
echo "检测到下载的规则文件没有更新,停止继续操作..." >$START_LOG
rm -rf /tmp/rules.yml >/dev/null 2>&1
echo "${LOGTIME} Updated Other Rules No Change, Do Nothing" >>/tmp/openclash.log
sleep 10
echo "" >$START_LOG
fi
else
echo "第三方规则下载失败,请检查网络或稍后再试!" >$START_LOG
rm -rf /tmp/rules.yml >/dev/null 2>&1
echo "${LOGTIME} Other Rules Update Error" >>/tmp/openclash.log
sleep 10
echo "" >$START_LOG
fi

View File

@ -0,0 +1,22 @@
#!/bin/sh
START_LOG="/tmp/openclash_start.log"
version_url="https://github.com/vernesong/OpenClash/raw/master/version"
echo "开始获取最新版本..." >$START_LOG
wget-ssl --no-check-certificate --timeout=3 --tries=2 "$version_url" -O /tmp/openclash_last_version
if [ "$?" -eq "0" ]; then
echo "版本获取成功..." >$START_LOG
if [ -f /etc/openclash/openclash_version ]; then
echo "对比版本信息..." >$START_LOG
if [ "$(sed -n 1p /etc/openclash/openclash_version)" = "$(sed -n 1p /tmp/openclash_last_version)" ]; then
echo "" >/tmp/openclash_last_version
echo "本地已安装最新版本!" >$START_LOG
sleep 10
fi
fi
echo "" >$START_LOG
else
echo "" >/tmp/openclash_last_version
echo "版本获取失败,请稍后再试!" >$START_LOG
sleep 10
echo "" >$START_LOG
fi

View File

@ -0,0 +1,18 @@
#!/bin/sh
status=`ps|grep -c openclash_watchdog.sh`
[ "$status" -gt "3" ] && echo "another clash_watchdog.sh is running,exit "
[ "$status" -gt "3" ] && exit 0
while :;
do
enable=$(uci get openclash.config.enable)
if [ "$enable" -eq 1 ]; then
if ! pidof clash >/dev/null; then
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
echo "${LOGTIME} Watchdog: OpenClash Problem, Restart " >>/tmp/openclash.log
/etc/init.d/openclash restart
fi
fi
sleep 60
done 2>/dev/null

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
._1EnK5MMInH{width:100%;height:100%;display:flex;justify-content:center;align-items:center}._39z9L5I2ao{--color1:#ddd;--size:40px;width:var(--size);height:var(--size);margin:10px;background-color:var(--color1);border-radius:100%;-webkit-animation:_1DSWK2a-pe 1s ease-in-out infinite;animation:_1DSWK2a-pe 1s ease-in-out infinite}@-webkit-keyframes _1DSWK2a-pe{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1);opacity:0}}@keyframes _1DSWK2a-pe{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1);opacity:0}}
._1rJPiLWN4s{position:fixed;top:0;bottom:0;left:0;right:0;overflow:hidden;padding:20px;background:var(--color-background);color:var(--color-text);text-align:center}._3h_IywJG1l{color:#2a477a;opacity:.6;display:flex;justify-content:center;padding:40px}.aXXDDfyTjE,._3h_IywJG1l{align-items:center}.aXXDDfyTjE{display:inline-flex;color:var(--color-text-secondary)}.aXXDDfyTjE:active,.aXXDDfyTjE:hover{color:#387cec}.aXXDDfyTjE svg{margin-right:5px}
._30oJwXNik9{background:var(--color-bg-sidebar);position:relative}._1SsCcpJvxN{display:block}._2r8EkOI78X{display:flex;align-items:center;justify-content:center;padding:25px 0 15px;color:#2a477a;transition:color .3s ease-in-out}@media (max-width:768px){._2r8EkOI78X{padding:0}}._2r8EkOI78X:hover{-webkit-animation:_2KRqAfqV8c .3s ease-in-out 0s infinite alternate;animation:_2KRqAfqV8c .3s ease-in-out 0s infinite alternate}._2r8EkOI78X img{width:80px;height:80px}@-webkit-keyframes _2KRqAfqV8c{0%{color:#2a477a}to{color:#1f52ac}}@keyframes _2KRqAfqV8c{0%{color:#2a477a}to{color:#1f52ac}}@media (max-width:768px){._2vUQ0Hs_C5{display:flex;justify-content:space-between;overflow:scroll}}._8mEn9Wlw1n{color:var(--color-text);text-decoration:none;display:flex;align-items:center;padding:8px 20px}@media (max-width:768px){._8mEn9Wlw1n{flex-direction:column}}._8mEn9Wlw1n svg{color:var(--color-icon)}._1WyHmd6t6y{background:var(--color-sb-active-row-bg)}@media (max-width:768px){._1WyHmd6t6y{background:none;border-bottom:2px solid #387cec}}._2eMIYGbP9O{padding-left:14px}@media (max-width:768px){._2eMIYGbP9O{padding-left:0;padding-top:5px}}.nURY8qkFLS{--sz:50px;position:absolute;bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:var(--sz);height:var(--sz);padding:20px 0;display:flex;justify-content:center;align-items:center}@media (max-width:768px){.nURY8qkFLS{display:none}}.nURY8qkFLS svg{display:block;color:var(--color-icon)}
.SNYKRrv_2I{height:76px;display:flex;align-items:center}.VG1cD2OYvg{padding:0 40px;text-align:left;margin:0}
._37kQcxVR4T{color:var(--color-text);display:flex;align-items:center}._1U3kUwas7v,.-Hr5Z5iYfF{padding-top:10px;padding-bottom:10px;width:200px}._1U3kUwas7v div:first-child,.-Hr5Z5iYfF div:first-child{color:var(--color-text-secondary)}._1U3kUwas7v div:nth-child(2),.-Hr5Z5iYfF div:nth-child(2){padding:10px 0 0;font-size:2em}.-Hr5Z5iYfF{padding-right:20px}._1U3kUwas7v{padding-left:20px}
._2rN7aLQPCl{padding:10px 40px}
._1u5AP7XMF9{padding:0 40px 5px}._2zeyKJDdFH{position:relative;height:40px}._3DQ7SXxKRA{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);left:0;width:100%}._1f-XUgRxH1{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:20px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 15px 0 35px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._3PLtWxZwrd{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);left:10px}
._2tpN_G7FeO{display:flex;align-items:center;flex-wrap:wrap;font-size:.9em;padding:10px}._3wuPHKqO5W{color:#eee;flex-shrink:0;text-align:center;width:66px;background:green;border-radius:5px;padding:3px 5px;margin:0 8px}.IwiVCclCSC{flex-shrink:0;color:#999;font-size:14px}._3I1beKAMFt{flex-shrink:0;display:flex;font-family:Roboto Mono,Menlo,monospace;align-items:center;padding:8px 0;width:100%;white-space:pre;overflow:auto}._2MDNI6JESq{margin:0;padding:0;color:var(--color-text)}._2MDNI6JESq li,._2MDNI6JESq li.even{background:var(--color-background)}._3KX1sKJ1QD{padding:10px 40px}._19_8g6kTIV{display:flex;flex-direction:column;align-items:center;justify-content:center;color:#2d2d30}._19_8g6kTIV div:nth-child(2){color:var(--color-text-secondary);font-size:1.4em;opacity:.6}._3ljFcrWmBC{opacity:.3}
input.nJQwngxVo8[type=checkbox]{--white:#fff;--grey:#d3d3d3;--color-theme:#047aff;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;background-color:darken(var(--white),2%);border:1px solid var(--grey);border-radius:26px;box-shadow:inset 0 0 0 1px var(--grey);cursor:pointer;height:28px;position:relative;transition:border .25s .15s,box-shadow .25s .3s,padding .25s;width:44px;vertical-align:top}input.nJQwngxVo8[type=checkbox]:after{background-color:var(--white);border:1px solid var(--grey);border-radius:24px;box-shadow:inset 0 -3px 3px rgba(0,0,0,.025),0 1px 4px rgba(0,0,0,.15),0 4px 4px rgba(0,0,0,.1);content:"";display:block;height:26px;left:0;position:absolute;right:16px;top:0;transition:border .25s .15s,left .25s .1s,right .15s .175s}input.nJQwngxVo8[type=checkbox]:checked{border-color:var(--color-theme);box-shadow:inset 0 0 0 13px var(--color-theme);padding-left:18px;transition:border .25s,box-shadow .25s,padding .25s .15s}input.nJQwngxVo8[type=checkbox]:checked:after{border-color:var(--color-theme);left:16px;right:0;transition:border .25s,left .15s .25s,right .25s .175s}
._3evbv-Ui87{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid #525252;color:var(--color-text);background:var(--color-toggle-bg);display:flex;position:relative}._3evbv-Ui87 input{position:absolute;left:0;opacity:0}._3evbv-Ui87 label{flex:1;z-index:2;display:flex;align-items:center;justify-content:center;padding:10px 0;cursor:pointer}._1ok8KIb1RH{z-index:1;position:absolute;display:block;left:0;height:100%;transition:left .2s ease-out;background:var(--color-toggle-selected)}
._2S85tjFa1n{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:4px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 15px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._2S85tjFa1n:focus{border-color:var(--color-focus-blue)}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
._2A0HoxnDqc{-webkit-appearance:none;outline:none;color:var(--color-btn-fg);background:var(--color-btn-bg);border:1px solid #555;border-radius:100px;padding:6px 12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}._2A0HoxnDqc:focus{border-color:var(--color-focus-blue)}._2A0HoxnDqc:hover{background:#387cec;border:1px solid #387cec;color:#fff}._2A0HoxnDqc:active{-webkit-transform:scale(.97);transform:scale(.97)}
._2id19fefQX{display:flex;flex-wrap:wrap}._2QQQyNTKoG{flex-grow:0;flex-wrap:0;margin-right:10px;margin-bottom:10px;cursor:pointer;border:2px solid transparent}.XJkW0wZSAx{border-color:#387cec}
._2OZZRrEL0J{padding:10px 40px 40px}._2OZZRrEL0J>div{width:360px}.lF_ZoyIdZN{padding:10px 40px 40px}.VduFBb2hWX{padding:0 40px}.VduFBb2hWX>div{border-top:1px dashed #373737}._2NQoBOQcGA{padding:16px 0}
.ctrHyq7uir{position:fixed;top:0;right:0;left:0;bottom:0;background:#444;z-index:1024}._17mHpKiOUD{outline:none;position:absolute;color:#ddd;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background:#444;padding:20px;border-radius:10px}
._1r-KsYFNaj{position:relative;padding:10px 0}._1r-KsYFNaj input{-webkit-appearance:none;background-color:transparent;background-image:none;border:none;border-radius:0;border-bottom:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 8px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._1r-KsYFNaj input:focus{border-color:var(--color-focus-blue)}._1r-KsYFNaj label{position:absolute;left:8px;bottom:22px;transition:-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out;transition:transform .15s ease-in-out,-webkit-transform .15s ease-in-out;-webkit-transform-origin:0 0;transform-origin:0 0}._1r-KsYFNaj input:focus+label,._1r-KsYFNaj label.Hn6h5kxOg7{-webkit-transform:scale(.75) translateY(-25px);transform:scale(.75) translateY(-25px)}._1r-KsYFNaj input:focus+label{color:var(--color-focus-blue)}
._3hz7LVhvUv:focus{outline:none}._3HF-KB9mgO{display:flex;justify-content:center;align-items:center}._3HF-KB9mgO ._3HzgPICn91{color:#2d2d30;opacity:.4;transition:opacity .4s}._3HF-KB9mgO ._3HzgPICn91:hover{opacity:.7}._1wpZuvoD5I{padding:30px 0 10px}.L7jTy-EFJ2{display:flex}.L7jTy-EFJ2 div{flex:1 1 auto}.L7jTy-EFJ2 div:nth-child(2){flex-grow:0;flex-basis:120px;margin-left:10px}._2fehqRU9GV{padding:30px 0 10px;display:flex;justify-content:flex-end;align-items:center}
._3D3ZNp4oBz{background:none;position:fixed;top:0;bottom:0;left:0;right:0;-webkit-transform:none;transform:none;padding:0;border-radius:0}.tgH3yv-xGR{position:absolute;top:10%;left:50%;-webkit-transform:translate(-50%);transform:translate(-50%);max-width:376px;margin:0 auto}._3MMuzHtwZL{background:#222}
@font-face{font-family:Roboto Mono;font-style:normal;font-weight:400;src:local("Roboto Mono"),local("RobotoMono-Regular"),url(https://cdn.jsdelivr.net/npm/@hsjs/fonts@0.0.1/robotomono/v5/L0x5DF4xlVMF-BfR8bXMIjhLq3-cXbKD.woff2) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}.border-bottom,.border-left,.border-top{position:relative}.border-top:before{top:0}.border-bottom:after,.border-top:before{position:absolute;content:"";height:1px;width:100%;-webkit-transform:scaleY(.5) translateZ(0);transform:scaleY(.5) translateZ(0);left:0;right:0;background:#555}.border-bottom:after{bottom:0}.border-left:before{position:absolute;content:"";height:100%;width:1px;-webkit-transform:scaleX(.5) translateZ(0);transform:scaleX(.5) translateZ(0);top:0;bottom:0;background:#555;left:0}*,:after,:before{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,PingFang SC,Microsoft YaHei,;margin:0;padding:0;--color-focus-blue:#1a73e8}body,body.dark{--color-background:#202020;--color-text:#ddd;--color-text-secondary:#ccc;--color-bg-sidebar:#2d2d30;--color-sb-active-row-bg:#494b4e;--color-input-bg:#2d2d30;--color-input-border:#3f3f3f;--color-toggle-bg:#353535;--color-toggle-selected:#181818;--color-icon:#c7c7c7;--color-btn-bg:#232323;--color-btn-fg:#bebebe}body.light{--color-background:#fbfbfb;--color-text:#222;--color-text-secondary:#646464;--color-bg-sidebar:#e7e7e7;--color-sb-active-row-bg:#d0d0d0;--color-input-bg:#fff;--color-input-border:silver;--color-toggle-bg:#fff;--color-toggle-selected:#d7d7d7;--color-icon:#5b5b5b;--color-btn-bg:#f4f4f4;--color-btn-fg:#101010}
._35EMVy62Je{display:flex;background:var(--color-background);color:var(--color-text);min-height:300px;height:100vh}@media (max-width:768px){._35EMVy62Je{flex-direction:column}}.AwL8oIubvP{flex-grow:1;overflow:auto}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{RnhZ:function(s,j,a){var e={"./af":"K/tc","./af.js":"K/tc","./ar":"jnO4","./ar-dz":"o1bE","./ar-dz.js":"o1bE","./ar-kw":"Qj4J","./ar-kw.js":"Qj4J","./ar-ly":"HP3h","./ar-ly.js":"HP3h","./ar-ma":"CoRJ","./ar-ma.js":"CoRJ","./ar-sa":"gjCT","./ar-sa.js":"gjCT","./ar-tn":"bYM6","./ar-tn.js":"bYM6","./ar.js":"jnO4","./az":"SFxW","./az.js":"SFxW","./be":"H8ED","./be.js":"H8ED","./bg":"hKrs","./bg.js":"hKrs","./bm":"p/rL","./bm.js":"p/rL","./bn":"kEOa","./bn.js":"kEOa","./bo":"0mo+","./bo.js":"0mo+","./br":"aIdf","./br.js":"aIdf","./bs":"JVSJ","./bs.js":"JVSJ","./ca":"1xZ4","./ca.js":"1xZ4","./cs":"PA2r","./cs.js":"PA2r","./cv":"A+xa","./cv.js":"A+xa","./cy":"l5ep","./cy.js":"l5ep","./da":"DxQv","./da.js":"DxQv","./de":"tGlX","./de-at":"s+uk","./de-at.js":"s+uk","./de-ch":"u3GI","./de-ch.js":"u3GI","./de.js":"tGlX","./dv":"WYrj","./dv.js":"WYrj","./el":"jUeY","./el.js":"jUeY","./en-SG":"zavE","./en-SG.js":"zavE","./en-au":"Dmvi","./en-au.js":"Dmvi","./en-ca":"OIYi","./en-ca.js":"OIYi","./en-gb":"Oaa7","./en-gb.js":"Oaa7","./en-ie":"4dOw","./en-ie.js":"4dOw","./en-il":"czMo","./en-il.js":"czMo","./en-nz":"b1Dy","./en-nz.js":"b1Dy","./eo":"Zduo","./eo.js":"Zduo","./es":"iYuL","./es-do":"CjzT","./es-do.js":"CjzT","./es-us":"Vclq","./es-us.js":"Vclq","./es.js":"iYuL","./et":"7BjC","./et.js":"7BjC","./eu":"D/JM","./eu.js":"D/JM","./fa":"jfSC","./fa.js":"jfSC","./fi":"gekB","./fi.js":"gekB","./fo":"ByF4","./fo.js":"ByF4","./fr":"nyYc","./fr-ca":"2fjn","./fr-ca.js":"2fjn","./fr-ch":"Dkky","./fr-ch.js":"Dkky","./fr.js":"nyYc","./fy":"cRix","./fy.js":"cRix","./ga":"USCx","./ga.js":"USCx","./gd":"9rRi","./gd.js":"9rRi","./gl":"iEDd","./gl.js":"iEDd","./gom-latn":"DKr+","./gom-latn.js":"DKr+","./gu":"4MV3","./gu.js":"4MV3","./he":"x6pH","./he.js":"x6pH","./hi":"3E1r","./hi.js":"3E1r","./hr":"S6ln","./hr.js":"S6ln","./hu":"WxRl","./hu.js":"WxRl","./hy-am":"1rYy","./hy-am.js":"1rYy","./id":"UDhR","./id.js":"UDhR","./is":"BVg3","./is.js":"BVg3","./it":"bpih","./it-ch":"bxKX","./it-ch.js":"bxKX","./it.js":"bpih","./ja":"B55N","./ja.js":"B55N","./jv":"tUCv","./jv.js":"tUCv","./ka":"IBtZ","./ka.js":"IBtZ","./kk":"bXm7","./kk.js":"bXm7","./km":"6B0Y","./km.js":"6B0Y","./kn":"PpIw","./kn.js":"PpIw","./ko":"Ivi+","./ko.js":"Ivi+","./ku":"JCF/","./ku.js":"JCF/","./ky":"lgnt","./ky.js":"lgnt","./lb":"RAwQ","./lb.js":"RAwQ","./lo":"sp3z","./lo.js":"sp3z","./lt":"JvlW","./lt.js":"JvlW","./lv":"uXwI","./lv.js":"uXwI","./me":"KTz0","./me.js":"KTz0","./mi":"aIsn","./mi.js":"aIsn","./mk":"aQkU","./mk.js":"aQkU","./ml":"AvvY","./ml.js":"AvvY","./mn":"lYtQ","./mn.js":"lYtQ","./mr":"Ob0Z","./mr.js":"Ob0Z","./ms":"6+QB","./ms-my":"ZAMP","./ms-my.js":"ZAMP","./ms.js":"6+QB","./mt":"G0Uy","./mt.js":"G0Uy","./my":"honF","./my.js":"honF","./nb":"bOMt","./nb.js":"bOMt","./ne":"OjkT","./ne.js":"OjkT","./nl":"+s0g","./nl-be":"2ykv","./nl-be.js":"2ykv","./nl.js":"+s0g","./nn":"uEye","./nn.js":"uEye","./pa-in":"8/+R","./pa-in.js":"8/+R","./pl":"jVdC","./pl.js":"jVdC","./pt":"8mBD","./pt-br":"0tRk","./pt-br.js":"0tRk","./pt.js":"8mBD","./ro":"lyxo","./ro.js":"lyxo","./ru":"lXzo","./ru.js":"lXzo","./sd":"Z4QM","./sd.js":"Z4QM","./se":"//9w","./se.js":"//9w","./si":"7aV9","./si.js":"7aV9","./sk":"e+ae","./sk.js":"e+ae","./sl":"gVVK","./sl.js":"gVVK","./sq":"yPMs","./sq.js":"yPMs","./sr":"zx6S","./sr-cyrl":"E+lV","./sr-cyrl.js":"E+lV","./sr.js":"zx6S","./ss":"Ur1D","./ss.js":"Ur1D","./sv":"X709","./sv.js":"X709","./sw":"dNwA","./sw.js":"dNwA","./ta":"PeUW","./ta.js":"PeUW","./te":"XLvN","./te.js":"XLvN","./tet":"V2x9","./tet.js":"V2x9","./tg":"Oxv6","./tg.js":"Oxv6","./th":"EOgW","./th.js":"EOgW","./tl-ph":"Dzi0","./tl-ph.js":"Dzi0","./tlh":"z3Vd","./tlh.js":"z3Vd","./tr":"DoHr","./tr.js":"DoHr","./tzl":"z1FC","./tzl.js":"z1FC","./tzm":"wQk9","./tzm-latn":"tT3J","./tzm-latn.js":"tT3J","./tzm.js":"wQk9","./ug-cn":"YRex","./ug-cn.js":"YRex","./uk":"raLr","./uk.js":"raLr","./ur":"UpQW","./ur.js":"UpQW","./uz":"Loxo","./uz-latn":"AQ68","./uz-latn.js":"AQ68","./uz.js":"Loxo","./vi":"KSF8","./vi.js":"KSF8","./x-pseudo":"/X5v","./x-pseudo.js":"/X5v","./yo":"fzPg","./yo.js":"fzPg","./zh-cn":"XDpg","./zh-cn.js":"XDpg","./zh-hk":"SatO","./zh-hk.js":"SatO","./zh-tw":"kOpN","./zh-tw.js":"kOpN"};function n(s){var j=r(s);return a(j)}function r(s){if(!a.o(e,s)){var j=new Error("Cannot find module '"+s+"'");throw j.code="MODULE_NOT_FOUND",j}return e[s]}n.keys=function(){return Object.keys(e)},n.resolve=r,s.exports=n,n.id="RnhZ"}}]);

View File

@ -0,0 +1 @@
[0522/053329.127:ERROR:crash_report_database_win.cc(428)] unexpected header

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="yacd.ico">
<link rel="icon" type="image/png" sizes="64x64" href="yacd-64.png">
<link rel="icon" type="image/png" sizes="128x128" href="yacd-128.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name=viewport content="width=device-width, initial-scale=1">
<meta name="application-name" content="Clash Dashboard">
<meta name="description" content="Clash Dashboard">
<meta name="theme-color" content="#202020">
<title>Clash Dashboard</title>
<link rel="prefetch" href="https://cdn.jsdelivr.net/npm/@hsjs/fonts@0.0.1/robotomono/v5/L0x5DF4xlVMF-BfR8bXMIjhLq3-cXbKD.woff2">
<meta property="og:image" content="https://user-images.githubusercontent.com/1166872/47304841-536f3d80-d65a-11e8-8908-1917127dafc5.png">
<meta property="og:site_name" content="yacd">
<meta property="og:type" content="object">
<meta property="og:description" content="Clash Dashboard">
<link href="vendors~app.31d7bcb28b9aaba65968.css" rel="stylesheet">
<link href="app.0a46527a296fe87c6f7a.css" rel="stylesheet">
<body>
<div id="app"></div>
<script src="runtime~app.ebef38faf2aa388ee472.js" type="text/javascript"></script>
<script src="react~app.2889399d0f29b961e8c2.js" type="text/javascript"></script>
<script src="vendors~app.3aefabaae8e43cd5bd7f.js" type="text/javascript"></script>
<script src="app.9b891454b1d74a441519.js" type="text/javascript"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
._2V-RqIAl7n{border-radius:20px;margin:10px 0;padding:3px 0;color:#eee}
.NpfXwxWAxo{position:relative;height:90px}._250hnPTi2J{position:absolute;bottom:0;left:20%;display:flex;align-items:center;justify-content:center}._28H2QsOrtc{position:absolute;top:0;left:0;width:100%;height:100%}._3kdi5nima5{width:100%;padding-right:20px;overflow:hidden;text-overflow:ellipsis;margin:10px 0;font-size:1.1em}
._3PCSxT0l14>h2{margin-top:0}._3PCSxT0l14>h2 span:nth-child(2){font-size:12px;color:#777;font-weight:400;margin:0 .3em}._1yYRIyvlRd{display:flex;flex-wrap:wrap}._1OcDlvlM5R{width:300px;padding:10px 5px;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out}._1OcDlvlM5R._3oAxPKtZFv{cursor:pointer}._1OcDlvlM5R._3oAxPKtZFv:hover{-webkit-transform:scale(1.1);transform:scale(1.1)}
._1myfcMimT9{padding:10px 40px}._1khaX45NQe{position:fixed;right:20px;bottom:20px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
._3eSLieOhVX{display:flex;align-items:center;padding:10px 40px}._2n1pW09UvV{width:40px;padding-right:15px;color:var(--color-text-secondary);opacity:.4}.t1XJIwvW7A{display:flex;align-items:center;font-size:12px;opacity:.8}._1fNf8kj0HA{padding:10px 0;font-family:Roboto Mono,Menlo,monospace;font-size:19px}._3yJmN0tON0{width:110px}
._2Tux7NhweE{position:fixed;right:20px;bottom:20px}

View File

@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{BVyM:function(e,t,a){"use strict";a.r(t);var n=a("ODXe"),r=a("q1tI"),c=a.n(r),l=a("u4Dv"),i=a("5Wrh"),u=a("iR1w"),s=a("DKqX"),o=a("17x9"),m=a.n(o),f=a("xrux"),d=a.n(f),p={_default:"#59caf9",DIRECT:"#f5bc41",REJECT:"#cb3166"};function v(e){var t=e.type,a=e.payload,n=e.proxy,r=e.id,l=function(e){var t=e.proxy,a=p._default;return p[t]&&(a=p[t]),{color:a}}({proxy:n});return c.a.createElement("div",{className:d.a.rule},c.a.createElement("div",{className:d.a.left},r),c.a.createElement("div",null,c.a.createElement("div",{className:d.a.b},a),c.a.createElement("div",{className:d.a.a},c.a.createElement("div",{className:d.a.type},t),c.a.createElement("div",{style:l},n))))}v.propTypes={id:m.a.number,type:m.a.string,payload:m.a.string,proxy:m.a.string};var b=v,E=a("II4a"),h=a("Tvb5"),y={updateSearchText:h.f},x=Object(E.a)({mapStateToProps:function(e){return{searchText:Object(h.e)(e)}},actions:y}),O=a("Kv4h"),j=a("mlDh"),w=a.n(j);a.d(t,"default",function(){return _});var N=30,R=function(e){return{rules:Object(h.d)(e)}},g={fetchRules:h.b,fetchRulesOnce:h.c};function T(e,t){return t[e].id}var D=Object(r.memo)(function(e){var t=e.index,a=e.style,n=e.data[t];return c.a.createElement("div",{style:a},c.a.createElement(b,n))},u.b);function _(){var e=Object(l.b)(g),t=e.fetchRulesOnce,a=e.fetchRules,o=Object(l.c)(R).rules;Object(r.useEffect)(function(){t()},[t]);var m=Object(O.a)(),f=Object(n.a)(m,2),d=f[0],p=f[1];return c.a.createElement("div",null,c.a.createElement(s.a,{title:"Rules"}),c.a.createElement(x,null),c.a.createElement("div",{ref:d,style:{paddingBottom:N}},c.a.createElement(u.a,{height:p-N,width:"100%",itemCount:o.length,itemSize:80,itemData:o,itemKey:T},D)),c.a.createElement("div",{className:w.a.fabgrp},c.a.createElement(i.a,{label:"Refresh",onClick:a})))}},mlDh:function(e,t,a){e.exports={fabgrp:"_2Tux7NhweE"}},xrux:function(e,t,a){e.exports={rule:"_3eSLieOhVX",left:"_2n1pW09UvV",a:"t1XJIwvW7A",b:"_1fNf8kj0HA",type:"_3yJmN0tON0"}}}]);

View File

@ -0,0 +1 @@
!function(e){function t(t){for(var n,o,u=t[0],i=t[1],f=t[2],l=0,d=[];l<u.length;l++)o=u[l],a[o]&&d.push(a[o][0]),a[o]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(s&&s(t);d.length;)d.shift()();return c.push.apply(c,f||[]),r()}function r(){for(var e,t=0;t<c.length;t++){for(var r=c[t],n=!0,o=1;o<r.length;o++){var i=r[o];0!==a[i]&&(n=!1)}n&&(c.splice(t--,1),e=u(u.s=r[0]))}return e}var n={},o={5:0},a={5:0},c=[];function u(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,u),r.l=!0,r.exports}u.e=function(e){var t=[];o[e]?t.push(o[e]):0!==o[e]&&{2:1,4:1}[e]&&t.push(o[e]=new Promise(function(t,r){for(var n=({1:"chartjs",2:"proxies",4:"rules",7:"vendors~chartjs"}[e]||e)+"."+{1:"3492c684ba7d8497de04",2:"462629c0ce55931a9066",4:"0051e218ebc63aef81f1",7:"60c4bfacb0e342e6819f",8:"c1eaea41bb4791c0be2b"}[e]+".css",a=u.p+n,c=document.getElementsByTagName("link"),i=0;i<c.length;i++){var f=(s=c[i]).getAttribute("data-href")||s.getAttribute("href");if("stylesheet"===s.rel&&(f===n||f===a))return t()}var l=document.getElementsByTagName("style");for(i=0;i<l.length;i++){var s;if((f=(s=l[i]).getAttribute("data-href"))===n||f===a)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var n=t&&t.target&&t.target.src||a,c=new Error("Loading CSS chunk "+e+" failed.\n("+n+")");c.code="CSS_CHUNK_LOAD_FAILED",c.request=n,delete o[e],d.parentNode.removeChild(d),r(c)},d.href=a,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){o[e]=0}));var r=a[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise(function(t,n){r=a[e]=[t,n]});t.push(r[2]=n);var c,i=document.createElement("script");i.charset="utf-8",i.timeout=120,u.nc&&i.setAttribute("nonce",u.nc),i.src=function(e){return u.p+""+({1:"chartjs",2:"proxies",4:"rules",7:"vendors~chartjs"}[e]||e)+"."+{1:"707c4e7f2cfb4dfd187a",2:"687ac8f8aaa8ce3509ca",4:"a68d49f3a80e76d735e1",7:"c9231179be225dfb8564",8:"45fc79574f18cfbcc680"}[e]+".js"}(e),c=function(t){i.onerror=i.onload=null,clearTimeout(f);var r=a[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,c=new Error("Loading chunk "+e+" failed.\n("+n+": "+o+")");c.type=n,c.request=o,r[1](c)}a[e]=void 0}};var f=setTimeout(function(){c({type:"timeout",target:i})},12e4);i.onerror=i.onload=c,document.head.appendChild(i)}return Promise.all(t)},u.m=e,u.c=n,u.d=function(e,t,r){u.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,t){if(1&t&&(e=u(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(u.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)u.d(r,n,function(t){return e[t]}.bind(null,n));return r},u.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(t,"a",t),t},u.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},u.p="",u.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],f=i.push.bind(i);i.push=t,i=i.slice();for(var l=0;l<i.length;l++)t(i[l]);var s=f;r()}([]);

View File

@ -0,0 +1 @@
/*! modern-normalize | MIT License | https://github.com/sindresorhus/modern-normalize */html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}hr{height:0}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{padding:0}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,48 @@
#!/bin/sh
if [ -z "$7" ]; then
sed -i "/^dns:$/a\ enhanced-mode: ${2}" "$8"
elif [ "$7" != "$2" ] && [ "$2" != "0" ]; then
sed -i "s/${7}$/${2}/" "$8"
fi
if [ "$2" = "fake-ip" ]; then
if [ ! -z "`grep "fake-ip-range:" "$8"`" ]; then
sed -i "/fake-ip-range:/c\ fake-ip-range: 198.18.0.1/16" "$8"
else
sed -i "/enhanced-mode:/a\ fake-ip-range: 198.18.0.1/16" "$8"
fi
else
sed -i '/fake-ip-range:/d' "$8" 2>/dev/null
fi
sed -i '/^##Custom DNS##$/d' "$8" 2>/dev/null
if [ ! -z "`grep "^redir-port:" "$8"`" ]; then
sed -i "/^redir-port:/c\redir-port: ${6}" "$8"
else
sed -i "3i\redir-port: ${6}" "$8"
fi
if [ ! -z "`grep "^external-controller:" "$8"`" ]; then
sed -i "/^external-controller:/c\external-controller: 0.0.0.0:${5}" "$8"
else
sed -i "3i\external-controller: 0.0.0.0:${5}" "$8"
fi
if [ ! -z "`grep "^secret:" "$8"`" ]; then
sed -i "/^secret:/c\secret: '${4}'" "$8"
else
sed -i "3i\secret: '${4}'" "$8"
fi
if [ ! -z "`grep "^ enable:" "$8"`" ]; then
sed -i "/^ enable:/c\ enable: true" "$8"
else
sed -i "/^dns:/a\ enable: true" "$8"
fi
if [ ! -z "`grep "^allow-lan:" "$8"`" ]; then
sed -i "/^allow-lan:/c\allow-lan: true" "$8"
else
sed -i "3i\allow-lan: true" "$8"
fi
if [ ! -z "`grep "^external-ui:" "$8"`" ]; then
sed -i '/^external-ui:/c\external-ui: "/usr/share/openclash/dashboard"' "$8"
else
sed -i '3i\external-ui: "/usr/share/openclash/dashboard"' "$8"
fi

View File

@ -0,0 +1,107 @@
#!/bin/sh
check_def=0
if [ "$2" != 0 ]; then
if [ ! -f /etc/openclash/"$2".yml ]; then
echo "${1} /etc/openclash/"$2".yml Not Exist, Will Use Self Rules, Please Update and Try Again" >>/tmp/clash.log
exit 0
else
rulesource=$(grep '##source:' "$4" |awk -F ':' '{print $2}')
[ "$rulesource" != "$2" ] && {
check_def=1
}
[ "$check_def" -ne 1 ] && {
grep "^##updated$" /etc/openclash/"$2".yml 1>/dev/null
[ "$?" -eq "0" ] && {
sed -i '/^##updated$/d' /etc/openclash/"$2".yml
check_def=1
}
}
[ "$check_def" -ne 1 ] && {
GlobalTV=$(grep '##GlobalTV:' "$4" |awk -F ':' '{print $2}')
AsianTV=$(grep '##AsianTV:' "$4" |awk -F ':' '{print $2}')
Proxy=$(grep '##Proxy:' "$4" |awk -F ':' '{print $2}')
Apple=$(grep '##Apple:' "$4" |awk -F ':' '{print $2}')
AdBlock=$(grep '##AdBlock:' "$4" |awk -F ':' '{print $2}')
Others=$(grep '##Others:' "$4" |awk -F ':' '{print $2}')
Domestic=$(grep '##Domestic:' "$4" |awk -F ':' '{print $2}')
if [ "$2" = "ConnersHua_return" ]; then
if [ "$(uci get openclash.config.Proxy)" != "$Proxy" ]\
|| [ "$(uci get openclash.config.Others)" != "$Others" ];then
check_def=1
fi
else
if [ "$(uci get openclash.config.GlobalTV)" != "$GlobalTV" ]\
|| [ "$(uci get openclash.config.AsianTV)" != "$AsianTV" ]\
|| [ "$(uci get openclash.config.Proxy)" != "$Proxy" ]\
|| [ "$(uci get openclash.config.Apple)" != "$Apple" ]\
|| [ "$(uci get openclash.config.AdBlock)" != "$AdBlock" ]\
|| [ "$(uci get openclash.config.Others)" != "$Others" ]\
|| [ "$(uci get openclash.config.Domestic)" != "$Domestic" ]; then
check_def=1
fi
fi
}
if [ "$check_def" -eq 1 ]; then
GlobalTV=$(uci get openclash.config.GlobalTV 2>/dev/null)
AsianTV=$(uci get openclash.config.AsianTV 2>/dev/null)
Proxy=$(uci get openclash.config.Proxy 2>/dev/null)
Apple=$(uci get openclash.config.Apple 2>/dev/null)
AdBlock=$(uci get openclash.config.AdBlock 2>/dev/null)
Domestic=$(uci get openclash.config.Domestic 2>/dev/null)
Others=$(uci get openclash.config.Others 2>/dev/null)
if [ "$2" = "lhie1" ]; then
sed -i '/^Rule:$/,$d' "$4"
cat /etc/openclash/lhie1.yml >> "$4"
sed -i -e "s/,GlobalTV$/,${GlobalTV}#d/g" -e "/Rule:/a\##GlobalTV:${GlobalTV}"\
-e "s/,AsianTV$/,${AsianTV}#d/g" -e "/Rule:/a\##AsianTV:${AsianTV}"\
-e "s/,Proxy$/,${Proxy}#d/g" -e "/Rule:/a\##Proxy:${Proxy}"\
-e "s/,Apple$/,${Apple}#d/g" -e "/Rule:/a\##Apple:${Apple}"\
-e "s/,AdBlock$/,${AdBlock}#d/g" -e "/Rule:/a\##AdBlock:${AdBlock}"\
-e "s/,Domestic$/,${Domestic}#d/g" -e "/Rule:/a\##Domestic:${Domestic}"\
-e "s/,Others$/,${Others}#d/g" -e "/Rule:/a\##Others:${Others}"\
-e "s/#d$//g" "$4"
elif [ "$2" = "ConnersHua" ]; then
sed -i '/^Rule:$/,$d' "$4"
cat /etc/openclash/ConnersHua.yml >> "$4"
sed -i -e "s/,ForeignMedia$/,${GlobalTV}#d/g" -e "/Rule:/a\##GlobalTV:${GlobalTV}"\
-e "s/,DomesticMedia$/,${AsianTV}#d/g" -e "/Rule:/a\##AsianTV:${AsianTV}"\
-e "s/,PROXY$/,${Proxy}#d/g" -e "/Rule:/a\##Proxy:${Proxy}"\
-e "s/,Apple$/,${Apple}#d/g" -e "/Rule:/a\##Apple:${Apple}"\
-e "s/,Hijacking$/,${AdBlock}#d/g" -e "/Rule:/a\##AdBlock:${AdBlock}"\
-e "s/,DIRECT$/,${Domestic}#d/g" -e "/Rule:/a\##Domestic:${Domestic}"\
-e "s/,Final$/,${Others}#d/g" -e "/Rule:/a\##Others:${Others}"\
-e "s/#d$//g" "$4"
else
sed -i '/^Rule:$/,$d' "$4"
cat /etc/openclash/ConnersHua_return.yml >> "$4"
sed -i -e "s/,PROXY$/,${Proxy}#d/g" -e "/Rule:/a\##Proxy:${Proxy}"\
-e "s/,DIRECT$/,${Others}#d/g" -e "/Rule:/a\##Others:${Others}"\
-e "s/#d$//g" "$4"
fi
fi
fi
elif [ "$2" = 0 ]; then
[ -f /etc/openclash/config.bak ] && {
grep '##source:' "$4" 1>/dev/null
if [ "$?" -eq "0" ]; then
cp /etc/openclash/config.bak /etc/openclash/configrules.bak
sed -i -n '/^Rule:$/,$p' /etc/openclash/configrules.bak
sed -i '/^Rule:$/,$d' "$4"
cat /etc/openclash/configrules.bak >> "$4"
rm -rf /etc/openclash/configrules.bak
fi
}
fi
sed -i '/^##Custom Rules$/,/^##Custom Rules End$/d' "$4" 2>/dev/null
sed -i '/^##Custom Rules$/d' "$4" 2>/dev/null
sed -i '/^##Custom Rules End$/d' "$4" 2>/dev/null
[ "$3" = 1 ] && {
sed -i '/^Rule:$/a\##Custom Rules End' "$4" 2>/dev/null
sed -i '/^Rule:$/a\##Custom Rules' "$4" 2>/dev/null
sed -i '/^##Custom Rules$/r/etc/config/openclash_custom_rules.list' "$4" 2>/dev/null
}

View File

@ -0,0 +1,278 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "clash"
msgstr "clash"
msgid "OpenClash"
msgstr "OpenClash"
msgid "A Clash Client For OpenWrt"
msgstr "一个运行在OpenWrt上的Clash客户端"
msgid "Technical Support"
msgstr "技术支持"
msgid "Clash Global State"
msgstr "运行状态"
msgid "Clash Settings"
msgstr "全局设置"
msgid "Config Normal"
msgstr "配置文件正常"
msgid "Config Abnormal"
msgstr "配置文件(异常/不存在)"
msgid "File Not Exist"
msgstr "文件不存在"
msgid "Not Set"
msgstr "未设置"
msgid "File No Update"
msgstr "原始/不存在(配置文件)"
msgid "Server Configuration"
msgstr "配置文件"
msgid "Server logs"
msgstr "服务器日志"
msgid "Logs"
msgstr "服务器日志"
msgid "Update Setting"
msgstr "更新设置"
msgid "General Settings"
msgstr "全局设置"
msgid "Enable Clash"
msgstr "启动 OpenClash"
msgid "Disable Clash"
msgstr "关闭 OpenClash"
msgid "Commit Configurations"
msgstr "保存配置"
msgid "Apply Configurations"
msgstr "应用配置"
msgid "Select Mode"
msgstr "运行模式"
msgid "redir-host"
msgstr "Redir-Host模式"
msgid "fake-ip"
msgstr "Fake-IP模式"
msgid "Will to Take Over Your General Settings, Network Error Try Flush DNS Cache"
msgstr "启用将接管配置文件的模式设置如客户端的网络无法连接请尝试清除DNS缓存"
msgid "Disable Mode Control"
msgstr "使用配置文件设定的模式"
msgid "Custom DNS Setting"
msgstr "自定义DNS服务器"
msgid "Use Custom Rules"
msgstr "添加并启用自定义规则"
msgid "Add Custom DNS Servers"
msgstr "添加自定义DNS服务器"
msgid "Set OpenClash Upstream DNS Resolve Server"
msgstr "自定义DNS上游服务器将替换配置文件内的DNS服务器设置"
msgid "Disable Custom DNS Setting"
msgstr "不使用自定义DNS服务器"
msgid "DNS Server Group"
msgstr "上游DNS服务器分组"
msgid "DNS Server IP"
msgstr "DNS服务器IP"
msgid "DNS Server Port"
msgstr "DNS服务器端口"
msgid "DNS Server Type"
msgstr "DNS服务器类型"
msgid "Enable Custom DNS Setting"
msgstr "使用自定义DNS服务器"
msgid "Rules Setting"
msgstr "规则设置"
msgid "Server Config"
msgstr "配置文件"
msgid "Upload Clash Configuration"
msgstr "上传配置文件"
msgid ""
"NB: Rename your config file to config.yml before upload. file will save to /"
"etc/openclash"
msgstr ""
"注意上传之前请将配置文件重命名为config.yml文件将保存到 /etc/openclash 文件夹"
msgid "Clash Redir Port, Please Make Sure Ports Available"
msgstr "OpenClash转发端口请确保端口可用"
msgid "Subscription Update"
msgstr "配置文件"
msgid "Subcription Url"
msgstr "订阅URL"
msgid "Update Subcription"
msgstr "更新订阅配置"
msgid "Update Configuration"
msgstr "点击更新配置文件"
msgid "Update time (every day)"
msgstr "更新时间(每天)"
msgid "Auto Update"
msgstr "自动更新"
msgid "After clash start running, wait a moment for servers to resolve,enjoy"
msgstr "运行后,请等待服务器上线"
msgid "Clash config redir-port"
msgstr "转发端口"
msgid "Server Subscription Address"
msgstr "服务器订阅地址"
msgid "Auto Update Server subscription"
msgstr "自动更新服务器订阅"
msgid "Daily Server subscription update time"
msgstr "每日服务器订阅更新时间"
msgid "Under Server Config You Can Manually Upload Configuration From PC"
msgstr "或在配置文件页面手动上载配置"
msgid "Changes to config file must be made from source"
msgstr "必须从源文件更改配置文件"
msgid "Dashboard Secret"
msgstr "管理页面登录密码"
msgid "Set Dashboard Secret"
msgstr "设置您的管理页面登录密码"
msgid "Dashboard hostname is Your Router IP. Dashboard:192.168.1.1/openclash"
msgstr "登录主机名是您的路由器IP, 管理页面地址示例:192.168.1.1/openclash、192.168.1.1:9090/ui"
msgid "Dashboard Port"
msgstr "管理页面端口"
msgid "GEOIP(By MaxMind) Update"
msgstr "GEOIP 数据库(By MaxMind)"
msgid "Update GEOIP Database"
msgstr "更新GEOIP数据库"
msgid "Start Update GEOIP Database"
msgstr "点击更新GEOIP数据库"
msgid "Other Rules Update(Only in Use)"
msgstr "第三方规则(仅对正在使用的规则生效)"
msgid "Enable Other Rules"
msgstr "第三方规则"
msgid "Use Other Rules"
msgstr "选择并启用第三方规则,将替换配置文件内的所有规则"
msgid "Disable Other Rules"
msgstr "不使用第三方规则"
msgid "lhie1 Rules"
msgstr "lhie1 规则"
msgid "ConnersHua Rules"
msgstr "ConnersHua 规则"
msgid "ConnersHua Return Rules"
msgstr "ConnersHua 回国规则"
msgid "Auto Update Other Rules"
msgstr "启用第三方规则自动更新"
msgid "Update Time (Every Week)"
msgstr "更新时间(每周)"
msgid "Every Monday"
msgstr "每周一"
msgid "Every Tuesday"
msgstr "每周二"
msgid "Every Wednesday"
msgstr "每周三"
msgid "Every Thursday"
msgstr "每周四"
msgid "Every Friday"
msgstr "每周五"
msgid "Every Saturday"
msgstr "每周六"
msgid "Every Sunday"
msgstr "每周日"
msgid "Update Other Rules"
msgstr "更新第三方规则"
msgid "Start Update Other Rules"
msgstr "点击更新第三方规则"
msgid "GlobalTV"
msgstr "国际流媒体流量"
msgid "AsianTV"
msgstr "亚洲流媒体流量"
msgid "Proxy"
msgstr "必须代理的流量"
msgid "Apple"
msgstr "苹果服务流量"
msgid "AdBlock"
msgstr "广告流量"
msgid "Domestic"
msgstr "国内流量"
msgid "Others"
msgstr "未匹配到规则的流量"
msgid "Choose Proxy Group, Base On Your Servers Group in config.yml"
msgstr "指定流量(策略)的代理方式服务器组信息来源于您的config.yml"
msgid "Custom Clash Rules"
msgstr "自定义规则"
msgid "Custom Clash Rules Here"
msgstr "设置自定义规则"
msgid "Disable Custom Clash Rules"
msgstr "不设置自定义规则"
msgid "Enable Custom Clash Rules"
msgstr "设置自定义规则"
msgid "For More Go Github:https://github.com/Dreamacro/clash"
msgstr "更多规则信息请前往Github( https://github.com/Dreamacro/clash )查询"

View File

@ -0,0 +1,12 @@
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

Binary file not shown.

View File

@ -0,0 +1,247 @@
/*
* lmo - Lua Machine Objects - PO to LMO conversion tool
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* 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);
}

View File

@ -0,0 +1,328 @@
/*
* lmo - Lua Machine Objects - Base functions
*
* Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
*
* 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;
}
}
}

View File

@ -0,0 +1,92 @@
/*
* lmo - Lua Machine Objects - General header
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* 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