re-add Xunlei Fast Dick package

This commit is contained in:
coolsnowwolf 2018-10-09 18:42:55 +08:00
parent ce2054ec45
commit 0ca8116312
16 changed files with 1752 additions and 0 deletions

View File

@ -0,0 +1,69 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-xlnetacc
PKG_VERSION:=1.0.3
PKG_RELEASE:=8
PKG_LICENSE:=GPLv2
PKG_MAINTAINER:=Sense <sensec@gmail.com>
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=LuCI Support for XLNetAcc
PKGARCH:=all
DEPENDS:=+jshn +wget +openssl-util
endef
define Package/$(PKG_NAME)/description
LuCI Support for XLNetAcc.
endef
define Build/Prepare
$(foreach po,$(wildcard ${CURDIR}/files/luci/i18n/*.po), \
po2lmo $(po) $(PKG_BUILD_DIR)/$(patsubst %.po,%.lmo,$(notdir $(po)));)
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
( . /etc/uci-defaults/luci-xlnetacc ) && rm -f /etc/uci-defaults/luci-xlnetacc
fi
exit 0
endef
define Package/$(PKG_NAME)/conffiles
/etc/config/xlnetacc
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
$(INSTALL_DATA) $(PKG_BUILD_DIR)/xlnetacc.*.lmo $(1)/usr/lib/lua/luci/i18n/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
$(INSTALL_DATA) ./files/luci/controller/*.lua $(1)/usr/lib/lua/luci/controller/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
$(INSTALL_DATA) ./files/luci/model/cbi/*.lua $(1)/usr/lib/lua/luci/model/cbi/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/xlnetacc
$(INSTALL_DATA) ./files/luci/view/xlnetacc/*.htm $(1)/usr/lib/lua/luci/view/xlnetacc/
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/root/etc/config/xlnetacc $(1)/etc/config/xlnetacc
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/root/etc/init.d/xlnetacc $(1)/etc/init.d/xlnetacc
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_BIN) ./files/root/etc/hotplug.d/iface/95-xlnetacc $(1)/etc/hotplug.d/iface/95-xlnetacc
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) ./files/root/etc/uci-defaults/luci-xlnetacc $(1)/etc/uci-defaults/luci-xlnetacc
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/root/usr/bin/xlnetacc.sh $(1)/usr/bin/xlnetacc.sh
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1,47 @@
module("luci.controller.xlnetacc", package.seeall)
function index()
if not nixio.fs.access("/etc/config/xlnetacc") then
return
end
entry({"admin", "services", "xlnetacc"},
firstchild(), _("XLNetAcc")).dependent = false
entry({"admin", "services", "xlnetacc", "general"},
cbi("xlnetacc"), _("Settings"), 1)
entry({"admin", "services", "xlnetacc", "log"},
template("xlnetacc/logview"), _("Log"), 2)
entry({"admin", "services", "xlnetacc", "status"}, call("action_status"))
entry({"admin", "services", "xlnetacc", "logdata"}, call("action_log"))
end
local function is_running()
return luci.sys.call("(ps | grep xlnetacc.sh | grep -v 'grep') >/dev/null" ) == 0
end
function action_status()
luci.http.prepare_content("application/json")
luci.http.write_json({
run_state = is_running(),
down_state = nixio.fs.readfile("/var/state/xlnetacc_down_state") or "",
up_state = nixio.fs.readfile("/var/state/xlnetacc_up_state") or ""
})
end
function action_log()
local uci = require "luci.model.uci".cursor()
local util = require "luci.util"
local log_data = { }
log_data.syslog = util.trim(util.exec("logread | grep xlnetacc"))
if uci:get("xlnetacc", "general", "logging") ~= "0" then
log_data.client = nixio.fs.readfile("/var/log/xlnetacc.log") or ""
end
uci:unload("xlnetacc")
luci.http.prepare_content("application/json")
luci.http.write_json(log_data)
end

View File

@ -0,0 +1,92 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "XLNetAcc"
msgstr "迅雷快鸟"
msgid "XLNetAcc is a Thunder joint broadband operators launched a commitment to help users solve the low broadband, slow Internet access, poor Internet experience of professional-grade broadband upgrade software."
msgstr "迅雷快鸟是迅雷联合宽带运营商推出的一款致力于帮助用户解决宽带低、网速慢、上网体验差的专业级宽带加速软件。"
msgid "Settings"
msgstr "设置"
msgid "Log"
msgstr "日志"
msgid "Running Status"
msgstr "运行状态"
msgid "XLNetAcc Running Status"
msgstr "快鸟运行状态"
msgid "DownLink Upgrade Status"
msgstr "下行提速状态"
msgid "UpLink Upgrade Status"
msgstr "上行提速状态"
msgid "RUNNING"
msgstr "运行中"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "No upgrade information"
msgstr "暂无提速信息"
msgid "General Settings"
msgstr "基本设置"
msgid "Enabled"
msgstr "启用"
msgid "Enable DownLink Upgrade"
msgstr "开启下行提速"
msgid "Enable UpLink Upgrade"
msgstr "开启上行提速"
msgid "Enable Logging"
msgstr "启用日志记录"
msgid "Enable verbose logging"
msgstr "启用详细日志"
msgid "Upgrade interface"
msgstr "指定提速接口"
msgid "XLNetAcc account"
msgstr "迅雷快鸟帐号"
msgid "XLNetAcc password"
msgstr "迅雷快鸟密码"
msgid "Does not store the plaintext password, automatically emptied after start."
msgstr "不存储明文密码,启动后自动清空。"
msgid "Encrypted password"
msgstr "加密后的密码"
msgid "Auto-generate in accordance with the plaintext password, do not modify it!"
msgstr "根据明文密码自动生成,请勿修改!"
msgid "Log Data"
msgstr "日志数据"
msgid "Loading..."
msgstr "正在加载..."
msgid "Refresh every 5 seconds."
msgstr "每 5 秒刷新。"
msgid "syslog:"
msgstr "系统日志:"
msgid "log file:"
msgstr "日志文件:"
msgid "No log data."
msgstr "无日志数据。"
msgid "Error get log data."
msgstr "获取日志数据失败。"

View File

@ -0,0 +1,36 @@
local m, s, o
local uci = luci.model.uci.cursor()
m = Map("xlnetacc", "%s - %s" %{translate("XLNetAcc"), translate("Settings")}, translate("XLNetAcc is a Thunder joint broadband operators launched a commitment to help users solve the low broadband, slow Internet access, poor Internet experience of professional-grade broadband upgrade software."))
m:append(Template("xlnetacc/status"))
s = m:section(NamedSection, "general", "general", translate("General Settings"))
s.anonymous = true
s.addremove = false
o = s:option(Flag, "enabled", translate("Enabled"))
o.rmempty = false
o = s:option(Flag, "down_acc", translate("Enable DownLink Upgrade"))
o = s:option(Flag, "up_acc", translate("Enable UpLink Upgrade"))
o = s:option(Flag, "logging", translate("Enable Logging"))
o.default = "1"
o = s:option(Flag, "verbose", translate("Enable verbose logging"))
o:depends("logging", "1")
o = s:option(ListValue, "network", translate("Upgrade interface"))
uci:foreach("network", "interface", function(section)
if section[".name"] ~= "loopback" then
o:value(section[".name"])
end
end)
o = s:option(Value, "account", translate("XLNetAcc account"))
o = s:option(Value, "password", translate("XLNetAcc password"))
o.password = true
return m

View File

@ -0,0 +1,49 @@
<% css = [[
#log_text {
padding: 10px;
text-align: left;
height: 500px;
overflow: auto;
}
#log_text pre {
word-break: break-all;
margin: 0;
}
.description {
color: #ffffff;
background-color: #0099ff;
}
]]
%>
<%+header%>
<div class="cbi-map">
<h2 name="content"><%:XLNetAcc%> - <%:Log Data%></h2>
<fieldset class="cbi-section">
<fieldset class="cbi-section-node">
<div id="log_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading...%>" style="vertical-align:middle" /><%:Collecting data...%></div>
<div style="text-align:right"><small><%:Refresh every 5 seconds.%></small></div>
</fieldset>
</fieldset>
</div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[
var log_id = document.getElementById('log_text');
XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "xlnetacc", "logdata")%>', null, function(x, data) {
if ( log_id && data ) {
log_id.innerHTML = String.format(
'<pre>%s%s%s%s</pre>',
'<span class="description"><strong><%:syslog:%></strong></span><br/>',
data.syslog || '<%:No log data.%>',
'<br/><br/><span class="description"><strong><%:log file:%></strong></span><br/>',
data.client || '<%:No log data.%>'
);
} else if ( log_id ) {
log_id.innerHTML = '<strong><%:Error get log data.%></strong>';
}
});
//]]></script>
<%+footer%>

View File

@ -0,0 +1,21 @@
<fieldset class="cbi-section">
<legend><%:Running Status%></legend>
<table width="100%" cellspacing="10" id="_xlnetacc_status_table">
<tr><td width="33%"><%:XLNetAcc Running Status%></td><td id="_run_state"><em><%:Collecting data...%></em></td></tr>
<tr><td width="33%"><%:DownLink Upgrade Status%></td><td id="_down_state"><em><%:Collecting data...%></em></td></tr>
<tr><td width="33%"><%:UpLink Upgrade Status%></td><td id="_up_state"><em><%:Collecting data...%></em></td></tr>
</table>
</fieldset>
<script type="text/javascript">//<![CDATA[
var run_state = document.getElementById('_run_state');
var down_state = document.getElementById('_down_state');
var up_state = document.getElementById('_up_state');
XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "xlnetacc", "status")%>', null, function(x, status) {
if ( x && x.status == 200 ) {
run_state.innerHTML = status.run_state ? '<em><b><font color=green><%:RUNNING%></font></b></em>' : '<em><b><font color=red><%:NOT RUNNING%></font></b></em>';
down_state.innerHTML = status.down_state ? status.down_state : '<%:No upgrade information%>';
up_state.innerHTML = status.up_state ? status.up_state : '<%:No upgrade information%>';
}
});
//]]></script>

View File

@ -0,0 +1,5 @@
config general 'general'
option enabled '0'
option network 'wan'

View File

@ -0,0 +1,13 @@
#!/bin/sh
/etc/init.d/xlnetacc enabled || exit 0
[ "$INTERFACE" != "$(uci get xlnetacc.general.network)" ] && exit 0
case "$ACTION" in
ifup)
/etc/init.d/xlnetacc start
;;
ifdown)
/etc/init.d/xlnetacc stop
;;
esac

View File

@ -0,0 +1,49 @@
#!/bin/sh /etc/rc.common
START=95
STOP=10
SERVICE_DAEMONIZE=1
NAME=xlnetacc
start() {
local retry=1
while pidof "${NAME}.sh" >/dev/null 2>&1; do
[ $retry -ge 10 ] && return 1 || let retry++
sleep 1
done
config_load "$NAME"
config_get_bool enabled "general" "enabled" 0
config_get_bool down_acc "general" "down_acc" 0
config_get_bool up_acc "general" "up_acc" 0
config_get network "general" "network"
config_get username "general" "account"
config_get password "general" "password"
( [ $enabled -eq 0 ] || [ $down_acc -eq 0 -a $up_acc -eq 0 ] || [ -z "$username" -o -z "$password" -o -z "$network" ] ) && return 2
logger -p "daemon.notice" -t "$NAME" "XLNetAcc is starting ..."
service_start /usr/bin/${NAME}.sh --start
}
stop() {
ps | grep xlnetacc.sh | grep -v 'grep' >/dev/null 2>&1 || return 1
local pid spid
for pid in $(ps | grep xlnetacc.sh | grep -v 'grep' | awk '{print $1}'); do
echo "Stop XLNetAcc process PID: $pid"
kill -9 $pid >/dev/null 2>&1
for spid in $(pgrep -P $pid "sleep"); do
echo "Stop XLNetAcc process SPID: $spid"
kill -9 $spid >/dev/null 2>&1
done
done
logger -p "daemon.notice" -t "$NAME" "XLNetAcc has stoped."
return 0
}
restart() {
rm -rf /tmp/state/xlnetacc*
stop && sleep 1
start
}

View File

@ -0,0 +1,22 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@xlnetacc[-1]
add ucitrack xlnetacc
set ucitrack.@xlnetacc[-1].init=xlnetacc
commit ucitrack
EOF
general=$(uci -q get xlnetacc.@general[-1])
if [ -z "$general" ]; then
uci -q add xlnetacc general
fi
if [ "$general"x != "general"x ]; then
uci -q batch <<-EOF >/dev/null
rename xlnetacc.@general[-1]="general"
commit xlnetacc
EOF
fi
rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
exit 0

View File

@ -0,0 +1,670 @@
#!/bin/sh
# 声明常量
readonly packageName='com.xunlei.vip.swjsq'
readonly protocolVersion=200
readonly businessType=68
readonly sdkVersion='2.1.1.177662'
readonly clientVersion='2.4.1.3'
readonly agent_xl="android-async-http/xl-acc-sdk/version-$sdkVersion"
readonly agent_down='okhttp/3.4.1'
readonly agent_up='android-async-http/xl-acc-sdk/version-1.0.0.1'
readonly client_type_down='android-swjsq'
readonly client_type_up='android-uplink'
# 声明全局变量
_bind_ip=
_http_cmd=
_peerid=
_devicesign=
_userid=
_loginkey=
_sessionid=
_portal_down=
_portal_up=
_dial_account=
access_url=
http_args=
user_agent=
link_cn=
lasterr=
sequence_xl=1000000
sequence_down=$(( $(date +%s) / 6 ))
sequence_up=$sequence_down
# 包含用于解析 JSON 格式返回值的函数
. /usr/share/libubox/jshn.sh
# 读取 UCI 设置相关函数
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2> /dev/null)
echo -n ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[-1].$2 2> /dev/null)
echo -n ${ret:=$3}
}
uci_get_by_bool() {
case $(uci_get_by_name "$1" "$2" "$3") in
1|on|true|yes|enabled) echo -n 1;;
*) echo -n 0;;
esac
}
# 日志和状态栏输出。1 日志文件, 2 系统日志, 4 详细模式, 8 下行状态栏, 16 上行状态栏, 32 失败状态
_log() {
local msg=$1
local flag=$2
[ -z "$msg" ] && return
[ -z "$flag" ] && flag=1
local timestamp=$(date +'%Y/%m/%d %H:%M:%S')
[ $logging -eq 0 -a $(( $flag & 1 )) -ne 0 ] && flag=$(( $flag ^ 1 ))
if [ $verbose -eq 0 -a $(( $flag & 4 )) -ne 0 ]; then
[ $(( $flag & 1 )) -ne 0 ] && flag=$(( $flag ^ 1 ))
[ $(( $flag & 2 )) -ne 0 ] && flag=$(( $flag ^ 2 ))
fi
if [ $down_acc -eq 0 -a $(( $flag & 8 )) -ne 0 ]; then
flag=$(( $flag ^ 8 ))
[ $up_acc -ne 0 ] && flag=$(( $flag | 16 ))
fi
if [ $up_acc -eq 0 -a $(( $flag & 16 )) -ne 0 ]; then
flag=$(( $flag ^ 16 ))
[ $down_acc -ne 0 ] && flag=$(( $flag | 8 ))
fi
[ $(( $flag & 1 )) -ne 0 ] && echo "$timestamp $msg" >> $LOGFILE 2> /dev/null
[ $(( $flag & 2 )) -ne 0 ] && logger -p "daemon.info" -t "$NAME" "$msg"
[ $(( $flag & 32 )) -eq 0 ] && local color="green" || local color="red"
[ $(( $flag & 8 )) -ne 0 ] && echo -n "<font color=$color>$timestamp $msg</font>" > $down_state_file 2> /dev/null
[ $(( $flag & 16 )) -ne 0 ] && echo -n "<font color=$color>$timestamp $msg</font>" > $up_state_file 2> /dev/null
}
# 清理日志
clean_log() {
[ $logging -eq 1 -a -f "$LOGFILE" ] || return
[ $(wc -l "$LOGFILE" | awk '{print $1}') -le 800 ] && return
_log "清理日志文件"
local logdata=$(tail -n 500 "$LOGFILE")
echo "$logdata" > $LOGFILE 2> /dev/null
unset logdata
}
# 获取接口IP地址
get_bind_ip() {
json_cleanup; json_load "$(ubus call network.interface.$network status 2> /dev/null)" >/dev/null 2>&1
json_select "ipv4-address" >/dev/null 2>&1; json_select 1 >/dev/null 2>&1
json_get_var _bind_ip "address"
if [ -z "$_bind_ip" -o "$_bind_ip"x == "0.0.0.0"x ]; then
_log "获取网络 $network IP地址失败"
return 1
else
_log "绑定IP地址: $_bind_ip"
return 0
fi
}
# 定义基本 HTTP 命令和参数
gen_http_cmd() {
_http_cmd="wget-ssl -nv -t 1 -T 5 -O - --no-check-certificate"
_http_cmd="$_http_cmd --bind-address=$_bind_ip"
}
# 生成设备标识
gen_device_sign() {
local ifname macaddr
while : ; do
ifname=$(uci get "network.$network.ifname" 2> /dev/null)
[ "${ifname:0:1}" == "@" ] && network="${ifname:1}" || break
done
[ -z "$ifname" ] && { _log "获取网络 $network 信息出错"; return; }
json_cleanup; json_load "$(ubus call network.device status {\"name\":\"$ifname\"} 2> /dev/null)" >/dev/null 2>&1
json_get_var macaddr "macaddr"
[ -z "$macaddr" ] && { _log "获取网络 $network MAC地址出错"; return; }
macaddr=$(echo -n "$macaddr" | awk '{print toupper($0)}')
# 计算peerID
local fake_peerid=$(awk -F- '{print toupper($5)}' '/proc/sys/kernel/random/uuid')
readonly _peerid="${fake_peerid}004V"
_log "_peerid is $_peerid" $(( 1 | 4 ))
# 计算devicesign
# sign = div.10?.device_id + md5(sha1(packageName + businessType + md5(a protocolVersion specific GUID)))
local fake_device_id=$(echo -n "${macaddr//:/}" | openssl dgst -md5 | awk '{print $2}')
local fake_device_sign=$(echo -n "${fake_device_id}${packageName}${businessType}c7f21687eed3cdb400ca11fc2263c998" \
| openssl dgst -sha1 | awk '{print $2}')
readonly _devicesign="div101.${fake_device_id}"$(echo -n "$fake_device_sign" | openssl dgst -md5 | awk '{print $2}')
_log "_devicesign is $_devicesign" $(( 1 | 4 ))
}
# 快鸟帐号通用参数
swjsq_json() {
let sequence_xl++
# 生成POST数据
json_init
json_add_string protocolVersion "$protocolVersion"
json_add_string sequenceNo "$sequence_xl"
json_add_string platformVersion '2'
json_add_string isCompressed '0'
json_add_string businessType "$businessType"
json_add_string clientVersion "$clientVersion"
json_add_string peerID "$_peerid"
json_add_string appName "ANDROID-$packageName"
json_add_string sdkVersion "${sdkVersion##*.}"
json_add_string devicesign "$_devicesign"
json_add_string deviceModel 'MI'
json_add_string deviceName 'Xiaomi Mi'
json_add_string OSVersion "7.1.1"
}
# 帐号登录
swjsq_login() {
swjsq_json
if [ -z "$_userid" -o -z "$_loginkey" ]; then
access_url='https://mobile-login.xunlei.com/login'
json_add_string userName "$username"
json_add_string passWord "$password"
json_add_string verifyKey
json_add_string verifyCode
else
access_url='https://mobile-login.xunlei.com/loginkey'
json_add_string userName "$_userid"
json_add_string loginKey "$_loginkey"
fi
json_close_object
local ret=$($_http_cmd --user-agent="$agent_xl" "$access_url" --post-data="$(json_dump)")
case $? in
0)
_log "login is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errorCode"
;;
2) lasterr=-2;;
4) lasterr=-3;;
*) lasterr=-1;;
esac
case ${lasterr:=-1} in
0)
json_get_var _userid "userID"
json_get_var _loginkey "loginKey"
json_get_var _sessionid "sessionID"
_log "_sessionid is $_sessionid" $(( 1 | 4 ))
local outmsg="帐号登录成功"; _log "$outmsg" $(( 1 | 8 ))
;;
15) # 身份信息已失效
_userid=; _loginkey=;;
-1)
local outmsg="帐号登录失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
-2)
local outmsg="Wget 参数解析错误,请更新 GNU Wget"; _log "$outmsg" $(( 1 | 8 | 32 ));;
-3)
local outmsg="Wget 网络通信失败,请稍候"; _log "$outmsg";;
*)
local errorDesc; json_get_var errorDesc "errorDesc"
local outmsg="帐号登录失败。错误代码: ${lasterr}"; \
[ -n "$errorDesc" ] && outmsg="${outmsg},原因: $errorDesc"; _log "$outmsg" $(( 1 | 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 帐号注销
swjsq_logout() {
swjsq_json
json_add_string userID "$_userid"
json_add_string sessionID "$_sessionid"
json_close_object
local ret=$($_http_cmd --user-agent="$agent_xl" 'https://mobile-login.xunlei.com/logout' --post-data="$(json_dump)")
_log "logout is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errorCode"
case ${lasterr:=-1} in
0)
_sessionid=
local outmsg="帐号注销成功"; _log "$outmsg" $(( 1 | 8 ));;
-1)
local outmsg="帐号注销失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
*)
local errorDesc; json_get_var errorDesc "errorDesc"
local outmsg="帐号注销失败。错误代码: ${lasterr}"; \
[ -n "$errorDesc" ] && outmsg="${outmsg},原因: $errorDesc"; _log "$outmsg" $(( 1 | 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 获取用户信息
swjsq_getuserinfo() {
[ $1 -eq 1 ] && local _vasid=14 || local _vasid=33
swjsq_json
json_add_string userID "$_userid"
json_add_string sessionID "$_sessionid"
json_add_string vasid "$_vasid"
json_close_object
local ret=$($_http_cmd --user-agent="$agent_xl" 'https://mobile-login.xunlei.com/getuserinfo' --post-data="$(json_dump)")
_log "getuserinfo $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errorCode"
[ $1 -eq 1 ] && local outmsg="下行提速会员" || local outmsg="上行提速会员"
case ${lasterr:=-1} in
0)
local index vasid isVip isYear expireDate can_upgrade
json_select "vipList" >/dev/null 2>&1
while : ; do
json_select ${index:=1} >/dev/null 2>&1
[ $? -ne 0 ] && break
json_get_var vasid "vasid"
json_get_var isVip "isVip"
json_get_var isYear "isYear"
json_get_var expireDate "expireDate"
json_select ".." >/dev/null 2>&1
let index++
([ $1 -eq 1 -a ${vasid:-0} -eq 2 ] || [ ${vasid:-0} -eq $_vasid ]) && \
[ ${isVip:-0} -eq 1 -o ${isYear:-0} -eq 1 ] && { can_upgrade=1; break; }
done
if [ ${can_upgrade:-0} -eq 1 ]; then
outmsg="获取${outmsg}信息成功。会员到期时间:${expireDate:0:4}-${expireDate:4:2}-${expireDate:6:2}"; \
_log "$outmsg" $(( 1 | $1 * 8 ))
else
if [ ${#expireDate} -ge 8 ]; then
outmsg="${outmsg}已到期。会员到期时间:${expireDate:0:4}-${expireDate:4:2}-${expireDate:6:2}"
else
outmsg="${outmsg}无效"
fi
_log "$outmsg" $(( 1 | $1 * 8 | 32 ))
[ $1 -eq 1 ] && down_acc=0 || up_acc=0
fi
;;
-1)
outmsg="获取${outmsg}信息失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
*)
local errorDesc; json_get_var errorDesc "errorDesc"
outmsg="获取${outmsg}信息失败。错误代码: ${lasterr}"; \
[ -n "$errorDesc" ] && outmsg="${outmsg},原因: $errorDesc"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 获取提速入口
swjsq_portal() {
xlnetacc_var $1
[ $1 -eq 1 ] && access_url='http://api.portal.swjsq.vip.xunlei.com:81/v2/queryportal' || \
access_url='http://api.upportal.swjsq.vip.xunlei.com/v2/queryportal'
local ret=$($_http_cmd --user-agent="$user_agent" "$access_url")
_log "portal $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errno"
case ${lasterr:=-1} in
0)
local interface_ip interface_port province sp
json_get_var interface_ip "interface_ip"
json_get_var interface_port "interface_port"
json_get_var province "province_name"
json_get_var sp "sp_name"
if [ $1 -eq 1 ]; then
_portal_down="http://$interface_ip:$interface_port/v2"
_log "_portal_down is $_portal_down" $(( 1 | 4 ))
else
_portal_up="http://$interface_ip:$interface_port/v2"
_log "_portal_up is $_portal_up" $(( 1 | 4 ))
fi
local outmsg="获取${link_cn}提速入口成功"; \
[ -n "$province" -a -n "$sp" ] && outmsg="${outmsg}。运营商:${province}${sp}"; _log "$outmsg" $(( 1 | $1 * 8 ))
;;
-1)
local outmsg="获取${link_cn}提速入口失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
*)
local message; json_get_var message "message"
local outmsg="获取${link_cn}提速入口失败。错误代码: ${lasterr}"; \
[ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 获取网络带宽信息
isp_bandwidth() {
xlnetacc_var $1
local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/bandwidth?${http_args%&dial_account=*}")
_log "bandwidth $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errno"
case ${lasterr:=-1} in
0)
# 获取带宽数据
local can_upgrade bind_dial_account dial_account stream cur_bandwidth max_bandwidth
[ $1 -eq 1 ] && stream="downstream" || stream="upstream"
json_get_var can_upgrade "can_upgrade"
json_get_var bind_dial_account "bind_dial_account"
json_get_var dial_account "dial_account"
json_select; json_select "bandwidth" >/dev/null 2>&1
json_get_var cur_bandwidth "$stream"
json_select; json_select "max_bandwidth" >/dev/null 2>&1
json_get_var max_bandwidth "$stream"
json_select
cur_bandwidth=$(expr ${cur_bandwidth:-0} / 1024)
max_bandwidth=$(expr ${max_bandwidth:-0} / 1024)
if [ -n "$bind_dial_account" -a "$bind_dial_account" != "$dial_account" ]; then
local outmsg="绑定宽带账号 $bind_dial_account 与当前宽带账号 $dial_account 不一致,请联系迅雷客服解绑(每月仅一次)"; \
_log "$outmsg" $(( 1 | 8 | 32 ))
down_acc=0; up_acc=0
elif [ $can_upgrade -eq 0 ]; then
local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
local outmsg="${link_cn}无法提速"; \
[ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ))
[ $1 -eq 1 ] && down_acc=0 || up_acc=0
elif [ $cur_bandwidth -ge $max_bandwidth ]; then
local outmsg="${link_cn}无需提速。当前带宽 ${cur_bandwidth}M超过最大可提升带宽 ${max_bandwidth}M"; \
_log "$outmsg" $(( 1 | $1 * 8 ))
[ $1 -eq 1 ] && down_acc=0 || up_acc=0
else
if [ -z "$_dial_account" -a -n "$dial_account" ]; then
_dial_account=$dial_account
_log "_dial_account is $_dial_account" $(( 1 | 4 ))
fi
local outmsg="${link_cn}可以提速。当前带宽 ${cur_bandwidth}M可提升至 ${max_bandwidth}M"; _log "$outmsg" $(( 1 | $1 * 8 ))
fi
;;
724) # 724 账号存在异常
lasterr=-2
local outmsg="获取${link_cn}网络带宽信息失败。原因: 您的账号存在异常,请联系迅雷客服反馈"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
3103) # 3103 线路暂不支持
lasterr=0
local province sp
json_get_var province "province_name"; json_get_var sp "sp_name"
local outmsg="${link_cn}无法提速。原因: ${province}${sp}线路暂不支持"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ))
[ $1 -eq 1 ] && down_acc=0 || up_acc=0
;;
-1)
local outmsg="获取${link_cn}网络带宽信息失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
*)
local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
local outmsg="获取${link_cn}网络带宽信息失败。错误代码: ${lasterr}"; \
[ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 发送带宽提速信号
isp_upgrade() {
xlnetacc_var $1
local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/upgrade?$http_args")
_log "upgrade $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errno"
case ${lasterr:=-1} in
0)
local bandwidth
json_select "bandwidth" >/dev/null 2>&1
json_get_var bandwidth "downstream"
bandwidth=$(expr ${bandwidth:-0} / 1024)
local outmsg="${link_cn}提速成功,带宽已提升到 ${bandwidth}M"; _log "$outmsg" $(( 1 | $1 * 8 ))
[ $1 -eq 1 ] && down_acc=2 || up_acc=2
;;
812) # 812 已处于提速状态
lasterr=0
local outmsg="${link_cn}提速成功,当前宽带已处于提速状态"; _log "$outmsg" $(( 1 | $1 * 8 ))
[ $1 -eq 1 ] && down_acc=2 || up_acc=2
;;
724) # 724 账号存在异常
lasterr=-2
local outmsg="${link_cn}提速失败。原因: 您的账号存在异常,请联系迅雷客服反馈"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
-1)
local outmsg="${link_cn}提速失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
*)
local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
local outmsg="${link_cn}提速失败。错误代码: ${lasterr}"; \
[ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 发送提速心跳信号
isp_keepalive() {
xlnetacc_var $1
local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/keepalive?$http_args")
_log "keepalive $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errno"
case ${lasterr:=-1} in
0)
local outmsg="${link_cn}心跳信号返回正常"; _log "$outmsg";;
513) # 513 提速通道不存在
lasterr=-2
local outmsg="${link_cn}提速超时,提速通道不存在"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
-1)
local outmsg="${link_cn}心跳信号发送失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
*)
local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
local outmsg="${link_cn}提速失效。错误代码: ${lasterr}"; \
[ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 发送带宽恢复信号
isp_recover() {
xlnetacc_var $1
local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/recover?$http_args")
_log "recover $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errno"
case ${lasterr:=-1} in
0)
local outmsg="${link_cn}带宽已恢复"; _log "$outmsg" $(( 1 | $1 * 8 ))
[ $1 -eq 1 ] && down_acc=1 || up_acc=1;;
-1)
local outmsg="${link_cn}带宽恢复失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
*)
local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
local outmsg="${link_cn}带宽恢复失败。错误代码: ${lasterr}"; \
[ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
esac
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 查询提速信息,未使用
isp_query() {
xlnetacc_var $1
local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/query_try_info?$http_args")
_log "query_try_info $1 is $ret" $(( 1 | 4 ))
json_cleanup; json_load "$ret" >/dev/null 2>&1
json_get_var lasterr "errno"
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 设置参数变量
xlnetacc_var() {
if [ $1 -eq 1 ]; then
let sequence_down++
access_url=$_portal_down
http_args="sequence=${sequence_down}&client_type=${client_type_down}-${clientVersion}&client_version=${client_type_down//-/}-${clientVersion}&chanel=umeng-10900011&time_and=$(date +%s)000"
user_agent=$agent_down
link_cn="下行"
else
let sequence_up++
access_url=$_portal_up
http_args="sequence=${sequence_up}&client_type=${client_type_up}-${clientVersion}&client_version=${client_type_up//-/}-${clientVersion}"
user_agent=$agent_up
link_cn="上行"
fi
http_args="${http_args}&peerid=${_peerid}&userid=${_userid}&sessionid=${_sessionid}&user_type=1&os=android-7.1.1"
[ -n "$_dial_account" ] && http_args="${http_args}&dial_account=${_dial_account}"
}
# 重试循环
xlnetacc_retry() {
if [ $# -ge 3 -a $3 -ne 0 ]; then
[ $2 -eq 1 -a $down_acc -ne $3 ] && return 0
[ $2 -eq 2 -a $up_acc -ne $3 ] && return 0
fi
local retry=1
while : ; do
lasterr=
eval $1 $2 && break # 成功
[ $# -ge 4 -a $retry -ge $4 ] && break || let retry++ # 重试超时
case $lasterr in
-1) sleep 5s;; # 服务器未响应
-2) break;; # 严重错误
*) sleep 3s;; # 其它错误
esac
done
[ ${lasterr:-0} -eq 0 ] && return 0 || return 1
}
# 注销已登录帐号
xlnetacc_logout() {
[ -z "$_sessionid" ] && return 2
[ $# -ge 1 ] && local retry=$1 || local retry=1
xlnetacc_retry 'isp_recover' 1 2 $retry
xlnetacc_retry 'isp_recover' 2 2 $retry
xlnetacc_retry 'swjsq_logout' 0 0 $retry
[ $down_acc -ne 0 ] && down_acc=1; [ $up_acc -ne 0 ] && up_acc=1
_sessionid=; _dial_account=
[ $lasterr -eq 0 ] && return 0 || return 1
}
# 中止信号处理
sigterm() {
_log "trap sigterm, exit" $(( 1 | 4 ))
xlnetacc_logout
rm -f "$down_state_file" "$up_state_file"
exit 0
}
# 初始化
xlnetacc_init() {
[ "$1" != "--start" ] && return 1
# 防止重复启动
local pid
for pid in $(pidof "${0##*/}"); do
[ $pid -ne $$ ] && return 1
done
# 读取设置
readonly NAME=xlnetacc
readonly LOGFILE=/var/log/${NAME}.log
readonly down_state_file=/var/state/${NAME}_down_state
readonly up_state_file=/var/state/${NAME}_up_state
down_acc=$(uci_get_by_bool "general" "down_acc" 0)
up_acc=$(uci_get_by_bool "general" "up_acc" 0)
readonly logging=$(uci_get_by_bool "general" "logging" 1)
readonly verbose=$(uci_get_by_bool "general" "verbose" 0)
network=$(uci_get_by_name "general" "network" "wan")
readonly username=$(uci_get_by_name "general" "account")
readonly password=$(uci_get_by_name "general" "password")
local enabled=$(uci_get_by_bool "general" "enabled" 0)
([ $enabled -eq 0 ] || [ $down_acc -eq 0 -a $up_acc -eq 0 ] || [ -z "$username" -o -z "$password" -o -z "$network" ]) && return 2
[ $logging -eq 1 ] && [ ! -d /var/log ] && mkdir -p /var/log
[ -f "$LOGFILE" ] && _log "------------------------------"
_log "迅雷快鸟正在启动..."
# 检查外部调用工具
command -v wget-ssl >/dev/null || { _log "GNU Wget 未安装"; return 3; }
local opensslchk=$(echo -n 'openssl' | openssl dgst -sha1 | awk '{print $2}')
[ "$opensslchk" != 'c898fa1e7226427010e329971e82c669f8d8abb4' ] && { _log "openssl-util 未安装或计算错误"; return 3; }
# 捕获中止信号
trap 'sigterm' INT # Ctrl-C
trap 'sigterm' QUIT # Ctrl-\
trap 'sigterm' TERM # kill
# 生成设备标识
gen_device_sign
[ ${#_peerid} -ne 16 -o ${#_devicesign} -ne 71 ] && return 4
clean_log
[ -d /var/state ] || mkdir -p /var/state
return 0
}
# 程序主体
xlnetacc_main() {
while : ; do
# 获取外网IP地址
xlnetacc_retry 'get_bind_ip'
gen_http_cmd
# 注销快鸟帐号
xlnetacc_logout 3 && sleep 3s
# 登录快鸟帐号
while : ; do
lasterr=
swjsq_login
case $lasterr in
0) break;; # 登录成功
-1) sleep 5s;; # 服务器未响应
-2) return 7;; # Wget 参数解析错误
-3) sleep 3s;; # Wget 网络通信失败
6) sleep 130m;; # 需要输入验证码
8) sleep 3m;; # 服务器系统维护
15) sleep 1s;; # 身份信息已失效
*) return 5;; # 登录失败
esac
done
# 获取用户信息
xlnetacc_retry 'swjsq_getuserinfo' 1 1
xlnetacc_retry 'swjsq_getuserinfo' 2 1
[ $down_acc -eq 0 -a $up_acc -eq 0 ] && break
# 获取提速入口
xlnetacc_retry 'swjsq_portal' 1 1
xlnetacc_retry 'swjsq_portal' 2 1
# 获取带宽信息
xlnetacc_retry 'isp_bandwidth' 1 1 10 || { sleep 3m; continue; }
xlnetacc_retry 'isp_bandwidth' 2 1 10 || { sleep 3m; continue; }
[ $down_acc -eq 0 -a $up_acc -eq 0 ] && break
# 带宽提速
xlnetacc_retry 'isp_upgrade' 1 1 10 || { sleep 3m; continue; }
xlnetacc_retry 'isp_upgrade' 2 1 10 || { sleep 3m; continue; }
# 心跳保持
# local retry=1
while : ; do
clean_log # 清理日志
sleep 10m
# [ $retry -ge 144 ] && break || let retry++ # 心跳最多保持24小时144=24*60/10
xlnetacc_retry 'isp_keepalive' 1 2 5 || break
xlnetacc_retry 'isp_keepalive' 2 2 5 || break
done
done
xlnetacc_logout
_log "无法提速,迅雷快鸟已停止。"
return 6
}
# 程序入口
xlnetacc_init "$@" && xlnetacc_main
exit $?

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

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