add luci-app-airwhu

This commit is contained in:
CN_SZTL 2019-07-29 03:11:45 +08:00
parent b08e4897a2
commit a3569b6af5
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
14 changed files with 1281 additions and 1 deletions

View File

@ -42,7 +42,8 @@ OpenAppFilter source: [destan19/OpenAppFilter](https://github.com/destan19/OpenA
luci-app-smartinfo source: [animefansxj/luci-app-smartinfo](https://github.com/animefansxj/luci-app-smartinfo).<br/>
luci-app-koolproxy source: [Baozisoftware/luci-app-koolproxy](https://github.com/Baozisoftware/luci-app-koolproxy).<br/>
luci-app-k3screenctrl source: [Hill-98/luci-app-k3screenctrl](https://github.com/Hill-98/luci-app-k3screenctrl).<br/>
luci-app-smstool source: [f8q8/luci-app-smstool-master](https://github.com/f8q8/luci-app-smstool-master).
luci-app-smstool source: [f8q8/luci-app-smstool-master](https://github.com/f8q8/luci-app-smstool-master).<br/>
luci-app-airwhu source: [KyleRicardo/luci-app-airwhu](https://github.com/KyleRicardo/luci-app-airwhu).
# License
### [GPL v3](https://www.gnu.org/licenses/gpl-3.0.html).

View File

@ -0,0 +1,51 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-airwhu
PKG_VERSION=1.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-airwhu
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
DEPENDS:=+mentohust +kmod-ipt-nat6
TITLE:=luci-app-airwhu
PKGARCH:=all
endef
define Package/luci-app-airwhu/description
LuCI web-interface for Ruijie 802.1X Client with IPv6 NAT.
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./* $(PKG_BUILD_DIR)/
po2lmo $(PKG_BUILD_DIR)/po/airwhu.zh-cn.po $(PKG_BUILD_DIR)/po/airwhu.zh-cn.lmo
endef
define Build/Compile
endef
define Package/luci-app-airwhu/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_DIR) $(1)/bin
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
$(INSTALL_CONF) ./files/root/etc/config/airwhu $(1)/etc/config/airwhu
$(INSTALL_BIN) ./files/root/etc/init.d/mentohust $(1)/etc/init.d/mentohust
$(INSTALL_BIN) ./files/root/etc/hotplug.d/iface/99-ipv6nat $(1)/etc/hotplug.d/iface/99-ipv6nat
$(INSTALL_BIN) ./files/root/bin/ipv6masq.sh $(1)/bin/ipv6masq.sh
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/airwhu.lua $(1)/usr/lib/lua/luci/model/cbi/airwhu.lua
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/airwhu.lua $(1)/usr/lib/lua/luci/controller/airwhu.lua
$(INSTALL_DATA) $(PKG_BUILD_DIR)/po/airwhu.zh-cn.lmo $(1)/usr/lib/lua/luci/i18n/airwhu.zh-cn.lmo
endef
$(eval $(call BuildPackage,luci-app-airwhu))

View File

@ -0,0 +1,41 @@
#!/bin/sh /etc/rc.common
#Author:KyleRicardo
#Email:shaoyz714@126.com
action=$1
if [ "$action"x = "install"x ]; then
uci delete network.globals.ula_prefix
uci set network.wan6.peerdns='0'
uci set network.wan6.dns='2001:da8:202:10::36'
uci delete network.lan.ip6assign
uci set network.lan.ip6addr='BABE:BABE:BABE:BABE::1/64'
uci set network.lan.ip6prefix='BABE:BABE:BABE:BABE::/64'
uci set dhcp.lan.ra_management='1'
uci set dhcp.lan.ra_default='1'
uci set $(uci show firewall|grep Allow-ICMPv6-Forward|sed -e 's/.name[^ ]*'//).enabled='0'
uci commit
elif [ "$action"x = "uninstall"x ]; then
uci set network.globals.ula_prefix='fdad:91b7:54bb::/48'
uci delete network.wan6.peerdns='0'
uci delete network.wan6.dns='2001:da8:202:10::36'
uci set network.lan.ip6assign='60'
uci delete network.lan.ip6addr='BABE:BABE:BABE:BABE::1/64'
uci delete network.lan.ip6prefix='BABE:BABE:BABE:BABE::/64'
uci delete dhcp.lan.ra_management='1'
uci delete dhcp.lan.ra_default='1'
uci delete $(uci show firewall|grep Allow-ICMPv6-Forward|sed -e 's/.name[^ ]*'//).enabled
uci commit
fi

View File

@ -0,0 +1,25 @@
config switch
option enableauth
option startwithboot
option enableipv6
config auth
option Username
option Password
option ifname
option IP
option Mask
option Gateway
option DNS
option PingHost
option Timeout
option EchoInterval
option RestartWait
option MaxFail
option StartMode
option DHCPMode
option DaemonMode
option ShowNotify
option Version
option DataFile
option DHCPScript

View File

@ -0,0 +1,56 @@
#!/bin/sh /etc/rc.common
#Author:KyleRicardo
#Email:shaoyz714@126.com
[ "$ACTION" = ifup ] || exit 0
iface=wan6
[ -z "$iface" -o "$INTERFACE" = "$iface" ] || exit 0
ip -6 route add `ip -6 route show default|sed -e 's/from [^ ]* //'`
logger -t IPv6 "Add IPv6 default route."
MAX_TRIES=99
WAN6_NAME="wan6"
WAN6_INTERFACE=$(uci get "network.$WAN6_NAME.ifname")
#eth0.2 by default
LAN_IP6PREFIX=$(uci get network.lan.ip6prefix)
#e.g. aaaa:bbbb:cccc:dddd::/64
#LAN_ULA_PREFIX=$(uci get network.globals.ula_prefix)
#e.g. ddc2:d512:65f5::/48
PROBE=0
COUNT=1
while [ $PROBE -eq 0 ]
do
if [ $COUNT -gt $MAX_TRIES ]
then
logger -t NAT6 "No IPv6 route found (reached retry limit $MAX_TRIES times)" && exit 1
fi
sleep 5
logger -t NAT6 "Probing IPv6 route ($COUNT time)"
COUNT=$((COUNT+1))
PROBE=$(route -A inet6 | grep -c '::/0')
done
#ip6tables -t nat -I POSTROUTING -s "$LAN_ULA_PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
ip6tables -t nat -I POSTROUTING -s "$LAN_IP6PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
#WAN6_GATEWAY=$(route -A inet6 -e | grep "$WAN6_INTERFACE" | awk '/::\/0/{print $2; exit}')
#get gateway from routing table. !!!Caution!!! May not work !
WAN6_GATEWAY=$(ifconfig eth0.2 | grep 'Global' | awk '{print $3}'| awk -F':' '{print $1":"$2":"$3":"$4"::1"}')
#caculate gateway from wan ipv6
#WAN6_GATEWAY=$(tracepath6 -n tv.byr.cn | grep ' 1: ' | awk 'NR==1 {print $2}')
#opkg install iputils-tracepath6 . change tv.byr.cn for faster site. e.g. ipv6.bjtu.edu.cn
#route -A inet6 add 2000::/3 gw "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
route -A inet6 add default gw "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
logger -t NAT6 "Done with IPv6 settings"

View File

@ -0,0 +1,49 @@
#!/bin/sh /etc/rc.common
#Author:KyleRicardo
#Email:shaoyz714@126.com
START=65
STOP=10
start()
{
if [ $(uci get airwhu.@switch[0].enableauth) ] ;
then
local Username=$(uci get airwhu.@auth[0].Username)
local Password=$(uci get airwhu.@auth[0].Password)
local ifname=$(uci get airwhu.@auth[0].ifname)
local IP=$(uci get airwhu.@auth[0].IP)
local Mask=$(uci get airwhu.@auth[0].Mask)
local Gateway=$(uci get airwhu.@auth[0].Gateway)
local DNS=$(uci get airwhu.@auth[0].DNS)
local PingHost=$(uci get airwhu.@auth[0].PingHost)
local Timeout=$(uci get airwhu.@auth[0].Timeout)
local EchoInterval=$(uci get airwhu.@auth[0].EchoInterval)
local RestartWait=$(uci get airwhu.@auth[0].RestartWait)
local MaxFail=$(uci get airwhu.@auth[0].MaxFail)
local StartMode=$(uci get airwhu.@auth[0].StartMode)
local DHCPMode=$(uci get airwhu.@auth[0].DHCPMode)
local DaemonMode=$(uci get airwhu.@auth[0].DaemonMode)
local ShowNotify=$(uci get airwhu.@auth[0].ShowNotify)
local Version=$(uci get airwhu.@auth[0].Version)
local DataFile=$(uci get airwhu.@auth[0].DataFile)
local DHCPScript=$(uci get airwhu.@auth[0].DHCPScript)
mentohust -u$Username -p$Password -n$ifname -b$DaemonMode -a$StartMode -d$DaemonMode -c"$DHCPScript" -w>/dev/null 2>&1
#Temporarily designed only for whu. To be continued...
else
stop
fi
}
stop()
{
sync
mentohust -k >/dev/null 2>&1
}
restart()
{
stop
start
}

View File

@ -0,0 +1,5 @@
module("luci.controller.airwhu", package.seeall)
function index()
entry({"admin", "services", "AirWHU"}, cbi("airwhu"), _("AirWHU"), 100)
end

View File

@ -0,0 +1,115 @@
--[[
LuCI - Lua Configuration Interface
LICENSE under GPLv3.
Copyright 2017 KyleRicardo[W.B.L.E. TeAm] <shaoyz714@126.com>
]]
--
require("luci.sys")
require("luci.tools.webadmin")
local IsOnAir = (luci.sys.call("pidof mentohust > /dev/null") == 0)
if IsOnAir then
state_msg = "<b><font color=\"green\">" .. translate("Running") .. "</font></b>"
else
state_msg = "<b><font color=\"red\">" .. translate("Not Running") .. "</font></b>"
end
m = Map("airwhu", translate("AirWHU"), translate("Configure Ruijie 802.1X client with IPv6 NAT based on Masquerade.") .. "<br /><br />" .. translate("Status") .. " : " .. state_msg)
s = m:section(TypedSection, "switch", translate("Global Switch"), translate("Configure global 802.1X Authentication and IPv6-NAT on-off."))
s.addremove = false
s.anonymous = true
s:option(Flag, "enableauth", translate("Enable 802.1X Auth"), translate("Enable or disable Ruijie 802.1X authentication."))
tmp = s:option(Flag, "startwithboot", translate("Start with boot"), translate("Start Ruijie 802.1X Authentication based on MentoHUST when the router is booting."))
tmp:depends("enableauth","1")
s:option(Flag, "enableipv6", translate("Enable IPv6 NAT"), translate("Enable IPv6 NAT pass-through based on ip6tables MASQUERADE."))
s = m:section(TypedSection, "auth", translate("Config Authentication"), translate("The options below are all of MentoHUST's arguments."))
s.anonymous = true
s.addremove = false
ur = s:option(Value, "Username", translate("Username"))
ur.rmempty = false
pw = s:option(Value, "Password", translate("Password"))
pw.password = true
pw.rmempty = false
wan_dev = luci.sys.exec("uci get network.wan.ifname")
wan_dev = string.sub(wan_dev,1,string.len(wan_dev)-1)
ifname = s:option(ListValue, "ifname", translate("Interfaces"))
ifname:value(wan_dev)
s:option(Value, "IP", translate("IP"), translate("default to localhost's IP")).default = "0.0.0.0"
s:option(Value, "Mask", translate("Netmask"), translate("default to localhost's netmask")).default = "255.255.255.0"
s:option(Value, "Gateway", translate("Gateway"), translate("default to 0.0.0.0")).default = "0.0.0.0"
s:option(Value, "DNS", translate("DNS"), translate("default to 0.0.0.0")).default = "0.0.0.0"
s:option(Value, "PingHost", translate("Ping host"), translate("default to 0.0.0.0,i.e. disable this function")).default = "0.0.0.0"
s:option(Value, "Timeout", translate("Authenticate timeout(s)"), translate("default to 8s")).default = "8"
s:option(Value, "EchoInterval", translate("Heartbeat timeout(s)"), translate("default to 30s")).default = "30"
s:option(Value, "RestartWait", translate("Timeout on failure(s)"), translate("default to 15s")).default = "15"
s:option(Value, "MaxFail", translate("Max failure times"), translate("0 means no limit,default to 8")).default = "8"
t = s:option(ListValue, "StartMode", translate("mulcast address"), translate("default to 1"))
t:value("0", translate("0(standard)"))
t:value("1", translate("1(ruijjie)"))
t:value("2", translate("2(saier)"))
t.default = "1"
t = s:option(ListValue, "DHCPMode", translate("DHCP type"), translate("default to 1"))
t:value("0", translate("0(not in used)"))
t:value("1", translate("1(secondary authenticate)"))
t:value("2", translate("2(post authenticate)"))
t:value("3", translate("3(pre authenticate)"))
t.default = "1"
t = s:option(ListValue, "DaemonMode", translate("run in daemon mode"), translate("default to 3"))
t:value("0", translate("0(no)"))
t:value("1", translate("1(yes,turn off output)"))
t:value("2", translate("2(yes,persevere output)"))
t:value("3", translate("3(yes,output to file)"))
t.default = "3"
s:option(Value, "ShowNotify", translate("display notification"), translate("0(no),1-20(yes),default to 0")).default = "0"
s:option(Value, "Version", translate("client version"), translate("default to 0.00,compatible with xrgsu")).default = "0.00"
tmp = s:option(Value, "DataFile", translate("customized data file"), translate("not in used by default"))
tmp.rmempty = true
script = s:option(Value, "DHCPScript", translate("DHCP script"), translate("use dhclient by default"))
script.default = "udhcpc -i "..wan_dev
local apply = luci.http.formvalue("cbi.apply")
if apply then
if luci.sys.exec("uci get airwhu.@switch[0].enableauth") then
luci.sys.exec("/etc/init.d/mentohust restart")
else
luci.sys.exec("/etc/init.d/mentohust stop")
end
if luci.sys.exec("uci get airwhu.@switch[0].startwithboot") then
luci.sys.exec("/etc/init.d/mentohust enable")
else
luci.sys.exec("/etc/init.d/mentohust disable")
end
if luci.sys.exec("uci get airwhu.@switch[0].enableipv6") then
luci.sys.exec("sh /bin/ipv6masq.sh install")
else
luci.sys.exec("sh /bin/ipv6masq.sh uninstall")
end
end
return m

View File

@ -0,0 +1,258 @@
msgid ""
msgstr ""
"Project-Id-Version: airwhu\n"
"POT-Creation-Date: 2017-05-11 19:39+0800\n"
"PO-Revision-Date: 2017-05-11 19:40+0800\n"
"Last-Translator: KyleRicardo <shaoyz714@126.com>\n"
"Language-Team: [W.B.L.E. TeAm] <shaoyz714@126.com>\n"
"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.8\n"
"X-Poedit-Basepath: ../files\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: translate\n"
"X-Poedit-SearchPath-0: .\n"
#: root/usr/lib/lua/luci/controller/airwhu.lua:4
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
msgid "AirWHU"
msgstr "AirWHU"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:14
msgid "Running"
msgstr "运行中"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:16
msgid "Not Running"
msgstr "未运行"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
msgid "Configure Ruijie 802.1X client with IPv6 NAT based on Masquerade."
msgstr "配置基于IPv6 Masquerade技术的锐捷802.1X认证网络。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
msgid "Status"
msgstr "状态"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:21
msgid "Global Switch"
msgstr "全局开关"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:21
msgid "Configure global 802.1X Authentication and IPv6-NAT on-off."
msgstr "配置全局802.1X认证与IPv6 NAT开关。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:25
msgid "Enable 802.1X Auth"
msgstr "开启锐捷认证"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:25
msgid "Enable or disable Ruijie 802.1X authentication."
msgstr "打开或关闭锐捷802.1X认证。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:26
msgid "Start with boot"
msgstr "开机启动"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:26
msgid ""
"Start Ruijie 802.1X Authentication based on MentoHUST when the router is "
"booting."
msgstr "开机自动启动基于MentoHUST的锐捷802.1X认证客户端。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:28
msgid "Enable IPv6 NAT"
msgstr "开启IPv6 NAT"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:28
msgid "Enable IPv6 NAT pass-through based on ip6tables MASQUERADE."
msgstr "开启基于ip6tables Masquerade技术的IPv6 NAT穿透。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:30
msgid "Config Authentication"
msgstr "配置认证参数"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:30
msgid "The options below are all of MentoHUST's arguments."
msgstr "以下是全部的MentoHUST认证参数。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:34
msgid "Username"
msgstr "用户名"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:36
msgid "Password"
msgstr "密码"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:41
msgid "Interfaces"
msgstr "网卡"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:51
msgid "IP"
msgstr "IP地址"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:51
msgid "default to localhost's IP"
msgstr "默认为localhost的IP地址"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:53
msgid "Netmask"
msgstr "掩码"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:53
msgid "default to localhost's netmask"
msgstr "默认为localhost的掩码"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:55
msgid "Gateway"
msgstr "网关"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:55
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:57
msgid "default to 0.0.0.0"
msgstr "默认为0.0.0.0"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:57
msgid "DNS"
msgstr "DNS服务器"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:59
msgid "Ping host"
msgstr "Ping主机"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:59
msgid "default to 0.0.0.0,i.e. disable this function"
msgstr "默认为0.0.0.0,即关闭该功能"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:61
msgid "Authenticate timeout(s)"
msgstr "认证超时(秒)"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:61
msgid "default to 8s"
msgstr "默认为8秒"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:63
msgid "Heartbeat timeout(s)"
msgstr "心跳包超时(秒)"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:63
msgid "default to 30s"
msgstr "默认为30秒"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:65
msgid "Timeout on failure(s)"
msgstr "失败等待(秒)"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:65
msgid "default to 15s"
msgstr "默认为15秒"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:67
msgid "Max failure times"
msgstr "允许失败次数"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:67
msgid "0 means no limit,default to 8"
msgstr "0表示无限制默认为8"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:69
msgid "mulcast address"
msgstr "多播地址"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:69
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:75
msgid "default to 1"
msgstr "默认为1"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:70
msgid "0(standard)"
msgstr "0标准"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:71
msgid "1(ruijjie)"
msgstr "1锐捷"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:72
msgid "2(saier)"
msgstr "2赛尔"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:75
msgid "DHCP type"
msgstr "DHCP方式"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:76
msgid "0(not in used)"
msgstr "0不使用"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:77
msgid "1(secondary authenticate)"
msgstr "1二次认证"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:78
msgid "2(post authenticate)"
msgstr "3认证后"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:79
msgid "3(pre authenticate)"
msgstr "3认证前"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:82
msgid "run in daemon mode"
msgstr "后台运行模式"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:82
msgid "default to 3"
msgstr "默认为3"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:83
msgid "0(no)"
msgstr "0不使用后台模式"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:84
msgid "1(yes,turn off output)"
msgstr "1后台模式关闭输出"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:85
msgid "2(yes,persevere output)"
msgstr "2后台模式保留输出"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:86
msgid "3(yes,output to file)"
msgstr "3后台模式输出到日志文件"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:89
msgid "display notification"
msgstr "显示通知"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:89
msgid "0(no),1-20(yes),default to 0"
msgstr "0不使用1-20使用默认为0"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:91
msgid "client version"
msgstr "客户端版本号"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:91
msgid "default to 0.00,compatible with xrgsu"
msgstr "默认为0.00表示兼容xrgsu"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:93
msgid "customized data file"
msgstr "自定义数据文件"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:93
msgid "not in used by default"
msgstr "默认不使用"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:96
msgid "DHCP script"
msgstr "DHCP脚本"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:96
msgid "use dhclient by default"
msgstr "默认使用dhclient"

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