Merge pull request #1240 from bobmseagithub/luci-app-statistics_Voltage_graphs
authorHannu Nyman <hannu.nyman@iki.fi>
Mon, 24 Jul 2017 05:02:18 +0000 (08:02 +0300)
committerGitHub <noreply@github.com>
Mon, 24 Jul 2017 05:02:18 +0000 (08:02 +0300)
luci-app-statistics: Voltage graphs - AC and DC

24 files changed:
applications/luci-app-firewall/luasrc/model/cbi/firewall/zone-details.lua
applications/luci-app-shadowsocks-libev/Makefile
applications/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua [deleted file]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po [deleted file]
applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po [deleted file]
applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot [deleted file]
applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po [deleted file]
applications/luci-app-travelmate/luasrc/controller/travelmate.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm
applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm [new file with mode: 0644]
modules/luci-base/htdocs/luci-static/resources/cbi.js
modules/luci-base/po/zh-cn/base.po

index 500d1bf..7553504 100644 (file)
@@ -21,7 +21,7 @@ nw.init(m.uci)
 
 local zone = fw:get_zone(arg[1])
 if not zone then
-       luci.http.redirect(dsp.build_url("admin/network/firewall/zones"))
+       luci.http.redirect(ds.build_url("admin/network/firewall/zones"))
        return
 else
        m.title = "%s - %s" %{
index 848a5c8..d0923e0 100644 (file)
@@ -1,14 +1,16 @@
 #
-# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
+# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
 #
 # This is free software, licensed under the Apache License, Version 2.0 .
 #
 
 include $(TOPDIR)/rules.mk
 
-LUCI_TITLE:=LuCI Support for Shadowsocks-libev
+LUCI_TITLE:=LuCI Support for shadowsocks-libev
 LUCI_DEPENDS:=
 
+PKG_LICENSE:=Apache-2.0
+
 include ../../luci.mk
 
 # call BuildPackage - OpenWrt buildroot signature
index ae96816..e191e59 100644 (file)
@@ -1,12 +1,22 @@
--- Copyright 2015 Jian Chang <aa65535@live.com>
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
 -- Licensed to the public under the Apache License 2.0.
-
+--
 module("luci.controller.shadowsocks-libev", package.seeall)
 
 function index()
-       if not nixio.fs.access("/etc/config/shadowsocks-libev") then
-               return
-       end
+       entry({"admin", "services", "shadowsocks-libev"},
+               alias("admin", "services", "shadowsocks-libev", "instances"),
+               _("Shadowsocks-libev"), 59)
+
+       entry({"admin", "services", "shadowsocks-libev", "instances"},
+               arcombine(cbi("shadowsocks-libev/instances"), cbi("shadowsocks-libev/instance-details")),
+               _("Local Instances"), 10).leaf = true
+
+       entry({"admin", "services", "shadowsocks-libev", "servers"},
+               cbi("shadowsocks-libev/servers"),
+               _("Remote Servers"), 20).leaf = true
 
-       entry({"admin", "services", "shadowsocks-libev"}, cbi("shadowsocks-libev"), _("ShadowSocks-libev"), 74).dependent = true
+       entry({"admin", "services", "shadowsocks-libev", "rules"},
+               cbi("shadowsocks-libev/rules"),
+               _("Redir Rules"), 30).leaf = true
 end
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua
deleted file mode 100644 (file)
index 97ce83f..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
--- Copyright 2015 Jian Chang <aa65535@live.com>
--- Licensed to the public under the Apache License 2.0.
-
-local m, s, o, e, a
-
-if luci.sys.call("pidof ss-redir >/dev/null") == 0 then
-       m = Map("shadowsocks-libev", translate("ShadowSocks-libev"), translate("ShadowSocks-libev is running"))
-else
-       m = Map("shadowsocks-libev", translate("ShadowSocks-libev"), translate("ShadowSocks-libev is not running"))
-end
-
-e = {
-       "table",
-       "rc4",
-       "rc4-md5",
-       "aes-128-cfb",
-       "aes-192-cfb",
-       "aes-256-cfb",
-       "bf-cfb",
-       "camellia-128-cfb",
-       "camellia-192-cfb",
-       "camellia-256-cfb",
-       "cast5-cfb",
-       "des-cfb",
-       "idea-cfb",
-       "rc2-cfb",
-       "seed-cfb",
-       "salsa20",
-       "chacha20",
-}
-
--- Global Setting
-s = m:section(TypedSection, "shadowsocks-libev", translate("Global Setting"))
-s.anonymous = true
-
-o = s:option(Flag, "enable", translate("Enable"))
-o.default = 1
-o.rmempty = false
-
-o = s:option(Value, "server", translate("Server Address"))
-o.datatype = "ipaddr"
-o.rmempty = false
-
-o = s:option(Value, "server_port", translate("Server Port"))
-o.datatype = "port"
-o.rmempty = false
-
-o = s:option(Value, "local_port", translate("Local Port"))
-o.datatype = "port"
-o.default = 1080
-o.rmempty = false
-
-o = s:option(Value, "timeout", translate("Connection Timeout"))
-o.datatype = "uinteger"
-o.default = 60
-o.rmempty = false
-
-o = s:option(Value, "password", translate("Password"))
-o.password = true
-o.rmempty = false
-
-o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
-for i,v in ipairs(e) do
-       o:value(v)
-end
-o.rmempty = false
-
-o = s:option(Value, "ignore_list", translate("Ignore List"))
-o:value("/dev/null", translate("Disabled"))
-o.default = "/dev/null"
-o.rmempty = false
-
--- UDP Relay
-s = m:section(TypedSection, "shadowsocks-libev", translate("UDP Relay"))
-s.anonymous = true
-
-o = s:option(ListValue, "udp_mode", translate("Relay Mode"))
-o:value("0", translate("Disabled"))
-o:value("1", translate("Enabled"))
-o:value("2", translate("Custom"))
-o.default = 0
-o.rmempty = false
-
-o = s:option(Value, "udp_server", translate("Server Address"))
-o.datatype = "ipaddr"
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_server_port", translate("Server Port"))
-o.datatype = "port"
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_local_port", translate("Local Port"))
-o.datatype = "port"
-o.default = 1081
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_timeout", translate("Connection Timeout"))
-o.datatype = "uinteger"
-o.default = 60
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_password", translate("Password"))
-o.password = true
-o:depends("udp_mode", 2)
-
-o = s:option(ListValue, "udp_encrypt_method", translate("Encrypt Method"))
-for i,v in ipairs(e) do
-       o:value(v)
-end
-o:depends("udp_mode", 2)
-
--- UDP Forward
-s = m:section(TypedSection, "shadowsocks-libev", translate("UDP Forward"))
-s.anonymous = true
-
-o = s:option(Flag, "tunnel_enable", translate("Enable"))
-o.default = 1
-o.rmempty = false
-
-o = s:option(Value, "tunnel_port", translate("UDP Local Port"))
-o.datatype = "port"
-o.default = 5300
-
-o = s:option(Value, "tunnel_forward", translate("Forwarding Tunnel"))
-o.default = "8.8.4.4:53"
-
--- Access Control
-s = m:section(TypedSection, "shadowsocks-libev", translate("Access Control"))
-s.anonymous = true
-
-s:tab("lan_ac", translate("LAN"))
-
-o = s:taboption("lan_ac", ListValue, "lan_ac_mode", translate("Access Control"))
-o:value("0", translate("Disabled"))
-o:value("1", translate("Allow listed only"))
-o:value("2", translate("Allow all except listed"))
-o.default = 0
-o.rmempty = false
-
-o = s:taboption("lan_ac", DynamicList, "lan_ac_ip", translate("LAN IP List"))
-o.datatype = "ipaddr"
-
-luci.ip.neighbors({ family = 4 }, function(entry)
-       if entry.reachable then
-               o:value(entry.dest:string())
-       end
-end)
-
-s:tab("wan_ac", translate("WAN"))
-
-o = s:taboption("wan_ac", DynamicList, "wan_bp_ip", translate("Bypassed IP"))
-o.datatype = "ip4addr"
-
-o = s:taboption("wan_ac", DynamicList, "wan_fw_ip", translate("Forwarded IP"))
-o.datatype = "ip4addr"
-
-return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua
new file mode 100644 (file)
index 0000000..d9a61d0
--- /dev/null
@@ -0,0 +1,49 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ds = require "luci.dispatcher"
+local ss = require "luci.model.shadowsocks-libev"
+
+local sname = arg[1]
+local redirect_url = ds.build_url("admin/services/shadowsocks-libev/instances")
+local s, o
+
+local m = Map("shadowsocks-libev")
+local sdata = m:get(sname)
+if not sdata then
+       luci.http.redirect(redirect_url)
+       return
+end
+local stype = sdata[".type"]
+m.redirect = redirect_url
+m.title = "shadowsocks-libev - %s - %s" % {stype, sname}
+
+
+s = m:section(NamedSection, sname, stype)
+s:tab("general", translate("General Settings"))
+s:tab("advanced", translate("Advanced Settings"))
+s:taboption("general", Flag, "disabled", translate("Disable"))
+ss.option_install_package(s, "general")
+
+if stype == "ss_server" then
+       ss.options_server(s, "general")
+       o = s:taboption("general", Value, "bind_address",
+               translate("Bind address"),
+               translate("The address ss-server will initiate connection from"))
+       o.datatype = "ipaddr"
+       o.placeholder = "0.0.0.0"
+       ss.values_ipaddr(o)
+       o = s:taboption("general", Value, "manager_address", translate("Manager address"))
+       o.datatype = "hostport"
+else
+       ss.options_client(s, "general")
+       if stype == "ss_tunnel" then
+               o = s:taboption("general", Value, "tunnel_address",
+                       translate("Tunnel address"),
+                       translate("The address ss-tunnel will forward traffic to"))
+               o.datatype = "hostport"
+       end
+end
+ss.options_common(s, "advanced")
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua
new file mode 100644 (file)
index 0000000..15e57df
--- /dev/null
@@ -0,0 +1,113 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ds = require "luci.dispatcher"
+local ss = require "luci.model.shadowsocks-libev"
+local ut = require "luci.util"
+local m, s, o
+
+m = Map("shadowsocks-libev",
+       translate("Local Instances"),
+       translate("Instances of shadowsocks-libev components, e.g. ss-local, \
+                          ss-redir, ss-tunnel, ss-server, etc.  To enable an instance it \
+                          is required to enable both the instance itself and the remote \
+                          server it refers to."))
+
+local instances = {}
+local cfgtypes = { "ss_local", "ss_redir", "ss_server", "ss_tunnel" }
+local instances_data = ut.ubus("service", "list", {name = "shadowsocks-libev"})["shadowsocks-libev"]
+if instances_data ~= nil then
+       instances_data = instances_data["instances"]
+end
+
+for sname, sdata in pairs(m:get()) do
+       local key, value = ss.cfgvalue_overview(sdata)
+       if key ~= nil then
+               if instances_data and instances_data[key] and instances_data[key]["running"] then
+                       value["running"] = "yes"
+               else
+                       value["running"] = "no"
+               end
+               instances[key] = value
+       end
+end
+
+s = m:section(Table, instances)
+s.addremove = true
+s.template_addremove = "shadowsocks-libev/add_instance"
+s.extedit = function(self, section)
+       local value = instances[section]
+       if type(value) == "table" then
+               return ds.build_url(unpack(ds.context.requestpath),
+                                       "services/shadowsocks-libev/instances",
+                                       value[".name"])
+       end
+end
+s.parse = function(self, ...)
+       Table.parse(self, ...)
+
+       local crval = REMOVE_PREFIX .. self.config
+       local name = self.map:formvaluetable(crval)
+       for k,v in pairs(name) do
+               local value = instances[k]
+               local sname = value[".name"]
+               if type(value) == "table" then
+                       m:del(sname)
+                       instances[k] = nil
+                       for _, oname in ipairs({"redir_tcp", "redir_udp"}) do
+                               local ovalue = m:get("ss_rules", oname)
+                               if ovalue == sname then
+                                       m:del("ss_rules", oname)
+                               end
+                       end
+               end
+       end
+
+       local stype = m:formvalue("_newinst.type")
+       local sname = m:formvalue("_newinst.name")
+       if ut.contains(cfgtypes, stype) then
+               local created
+               if sname and #sname > 0 then
+                       created = m:set(sname, nil, stype)
+               else
+                       created = m:add(stype)
+                       sname = created
+               end
+               if created then
+                       m.uci:save("shadowsocks-libev")
+                       luci.http.redirect(ds.build_url(
+                               "admin/services/shadowsocks-libev/instances", sname
+                       ))
+               end
+       end
+end
+
+o = s:option(DummyValue, "name", translate("Name"))
+o.rawhtml = true
+o = s:option(DummyValue, "overview", translate("Overview"))
+o.rawhtml = true
+
+s:option(DummyValue, "running", translate("Running"))
+
+o = s:option(Button, "disabled", translate("Enable/Disable"))
+o.render = function(self, section, scope)
+       if instances[section].disabled then
+               self.title = translate("Disabled")
+               self.inputstyle = "reset"
+       else
+               self.title = translate("Enabled")
+               self.inputstyle = "save"
+       end
+       Button.render(self, section, scope)
+end
+o.write = function(self, section)
+       local sdata = instances[section]
+       if type(sdata) == "table" then
+               local sname = sdata[".name"]
+               local disabled = not sdata["disabled"]
+               sdata["disabled"] = disabled
+               m:set(sname, "disabled", tostring(disabled))
+       end
+end
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua
new file mode 100644 (file)
index 0000000..fe5f9c3
--- /dev/null
@@ -0,0 +1,73 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ss = require("luci.model.shadowsocks-libev")
+
+local m, s, o
+
+m = Map("shadowsocks-libev",
+       translate("Redir Rules"),
+       translate("On this page you can configure how traffics are to be \
+               forwarded to ss-redir instances. \
+               If enabled, packets will first have their source ip addresses checked \
+               against <em>Src ip bypass</em>, <em>Src ip forward</em>, \
+               <em>Src ip checkdst</em> and if none matches <em>Src default</em> \
+               will give the default action to be taken. \
+               If the prior check results in action <em>checkdst</em>, packets will continue \
+               to have their destination addresses checked."))
+
+
+s = m:section(NamedSection, "ss_rules", "ss-rules")
+s:tab("general", translate("General Settings"))
+s:tab("srcip", translate("Source Settings"))
+s:tab("dstip", translate("Destination Settings"))
+
+s:taboption('general', Flag, "disabled", translate("Disable"))
+ss.option_install_package(s, 'general')
+
+o = s:taboption('general', ListValue, "redir_tcp",
+       translate("ss-redir for TCP"))
+ss.values_redir(o, 'tcp')
+o = s:taboption('general', ListValue, "redir_udp",
+       translate("ss-redir for UDP"))
+ss.values_redir(o, 'udp')
+
+o = s:taboption('general', ListValue, "local_default",
+       translate("Local-out default"),
+       translate("Default action for locally generated packets"))
+ss.values_actions(o)
+s:taboption('general', Value, "ipt_args",
+       translate("Extra arguments"),
+       translate("Passes additional arguments to iptables. Use with care!"))
+
+s:taboption('srcip', DynamicList, "src_ips_bypass",
+       translate("Src ip bypass"),
+       translate("Bypass redir action for packets with source addresses in this list"))
+s:taboption('srcip', DynamicList, "src_ips_forward",
+       translate("Src ip forward"),
+       translate("Go through redir action for packets with source addresses in this list"))
+s:taboption('srcip', DynamicList, "src_ips_checkdst",
+       translate("Src ip checkdst"),
+       translate("Continue to have dst address checked for packets with source addresses in this list"))
+o = s:taboption('srcip', ListValue, "src_default",
+       translate("Src default"),
+       translate("Default action for packets whose source addresses do not match any of the source ip list"))
+ss.values_actions(o)
+
+s:taboption('dstip', DynamicList, "dst_ips_bypass",
+       translate("Dst ip bypass"),
+       translate("Bypass redir action for packets with destination addresses in this list"))
+s:taboption('dstip', DynamicList, "dst_ips_forward",
+       translate("Dst ip forward"),
+       translate("Go through redir action for packets with destination addresses in this list"))
+
+o = s:taboption('dstip', FileBrowser, "dst_ips_bypass_file",
+       translate("Dst ip bypass file"),
+       translate("File containing ip addresses for the purposes as with <em>Dst ip bypass</em>"))
+o.datatype = "file"
+s:taboption('dstip', FileBrowser, "dst_ips_forward_file",
+       translate("Dst ip forward file"),
+       translate("File containing ip addresses for the purposes as with <em>Dst ip forward</em>"))
+o.datatype = "file"
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua
new file mode 100644 (file)
index 0000000..71c6656
--- /dev/null
@@ -0,0 +1,31 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ds = require "luci.dispatcher"
+local ss = require("luci.model.shadowsocks-libev")
+
+local m, s
+
+m = Map("shadowsocks-libev",
+       translate("Remote Servers"),
+       translate("Definition of remote shadowsocks servers.  \
+                       Disable any of them will also disable instances refering to it."))
+
+local sname = arg[1]
+if sname then
+       if not m:get(sname) then
+               luci.http.redirect(ds.build_url("admin/services/shadowsocks-libev/servers"))
+               return
+       end
+       s = m:section(NamedSection, sname, "server")
+       m.title = m.title .. ' - ' .. sname
+else
+       s = m:section(TypedSection, "server")
+       s.template = 'cbi/tblsection'
+       s.addremove = true
+end
+
+s:option(Flag, "disabled", translate("Disable"))
+ss.options_server(s)
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua
new file mode 100644 (file)
index 0000000..2753f45
--- /dev/null
@@ -0,0 +1,253 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local _up = getfenv(3)
+local ut = require("luci.util")
+local ds = require("luci.dispatcher")
+local nw = require("luci.model.network")
+nw.init()
+module("luci.model.shadowsocks-libev", function(m)
+       setmetatable(m, {__index=function (self, k)
+               local tb = _up
+               return rawget(self, k) or _up[k]
+       end})
+end)
+
+function values_actions(o)
+       for _, a in ipairs(actions) do
+               o:value(a)
+       end
+end
+
+function values_redir(o, xmode)
+       o.map.uci.foreach("shadowsocks-libev", "ss_redir", function(sdata)
+               local sname = sdata[".name"]
+               local mode = sdata["mode"]
+               if mode and mode:find(xmode) then
+                       local desc = "%s - %s" % {sname, mode}
+                       o:value(sname, desc)
+               end
+       end)
+end
+
+function values_serverlist(o)
+       o.map.uci.foreach("shadowsocks-libev", "server", function(sdata)
+               local sname = sdata[".name"]
+               local server = sdata["server"]
+               local server_port = sdata["server_port"]
+               if server and server_port then
+                       local desc = "%s - %s:%s" % {sname, sdata["server"], sdata["server_port"]}
+                       o:value(sname, desc)
+               end
+       end)
+end
+
+function values_ipaddr(o)
+       local keys, vals = {}, {}
+       for _, v in ipairs(nw:get_interfaces()) do
+               for _, a in ipairs(v:ipaddrs()) do
+                       o:value(a:host():string(), '%s (%s)' %{ a:host(), v:shortname() })
+               end
+       end
+end
+
+function options_client(s, tab)
+       local o
+
+       o = s:taboption(tab, ListValue, "server", translate("Remote server"))
+       values_serverlist(o)
+       o = s:taboption(tab, Value, "local_address", translate("Local address"))
+       o.datatype = "ipaddr"
+       o.placeholder = "0.0.0.0"
+       values_ipaddr(o)
+       o = s:taboption(tab, Value, "local_port", translate("Local port"))
+       o.datatype = "port"
+end
+
+function options_server(s, tab)
+       local o
+       local optfunc
+
+       if tab == nil then
+               optfunc = function(...) return s:option(...) end
+       else
+               optfunc = function(...) return s:taboption(tab, ...) end
+       end
+
+       o = optfunc(Value, "server", translate("Server"))
+       o.datatype = "host"
+       o.size = 16
+       o = optfunc(Value, "server_port", translate("Server port"))
+       o.datatype = "port"
+       o.size = 5
+       o = optfunc(ListValue, "method", translate("Method"))
+       for _, m in ipairs(methods) do
+               o:value(m)
+       end
+       o = optfunc(Value, "key", translate("Key (base64 encoding)"))
+       o.datatype = "base64"
+       o.password = true
+       o.size = 12
+       o = optfunc(Value, "password", translate("Password"))
+       o.password = true
+       o.size = 12
+end
+
+function options_common(s, tab)
+       local o
+
+       o = s:taboption(tab, ListValue, "mode", translate("Mode of operation"))
+       for _, m in ipairs(modes) do
+               o:value(m)
+       end
+       o.default = "tcp_and_udp"
+       o = s:taboption(tab, Value, "mtu", translate("MTU"))
+       o.datatype = "uinteger"
+       o = s:taboption(tab, Value, "timeout", translate("Timeout (sec)"))
+       o.datatype = "uinteger"
+       s:taboption(tab, Value, "user", translate("Run as"))
+
+       s:taboption(tab, Flag, "verbose", translate("Verbose"))
+       s:taboption(tab, Flag, "fast_open", translate("Enable TCP Fast Open"))
+       s:taboption(tab, Flag, "reuse_port", translate("Enable SO_REUSEPORT"))
+end
+
+function ucival_to_bool(val)
+       return val == "true" or val == "1" or val == "yes" or val == "on"
+end
+
+function cfgvalue_overview(sdata)
+       local stype = sdata[".type"]
+       local lines  = {}
+
+       if stype == "ss_server" then
+               cfgvalue_overview_(sdata, lines, names_options_server)
+               cfgvalue_overview_(sdata, lines, names_options_common)
+               cfgvalue_overview_(sdata, lines, {
+                       "bind_address",
+                       "manager_address",
+               })
+       elseif stype == "ss_local" or stype == "ss_redir" or stype == "ss_tunnel" then
+               cfgvalue_overview_(sdata, lines, names_options_client)
+               if stype == "ss_tunnel" then
+                       cfgvalue_overview_(sdata, lines, {"tunnel_address"})
+               end
+               cfgvalue_overview_(sdata, lines, names_options_common)
+       else
+               return nil, nil
+       end
+       local sname = sdata[".name"]
+       local key = "%s.%s" % {stype, sname}
+       local value = {
+               [".name"] = sname,
+               name = '%s.<var>%s</var>' % {stype, sname},
+               overview = table.concat(lines, "</br>"),
+               disabled = ucival_to_bool(sdata["disabled"]),
+       }
+       return key, value
+end
+
+function cfgvalue_overview_(sdata, lines, names)
+       local line
+
+       for _, n in ipairs(names) do
+               local v = sdata[n]
+               if v ~= nil then
+                       local fv = "<var>%s</var>" % ut.pcdata(v)
+                       if sdata[".type"] ~= "ss_server" and n == "server" then
+                               fv = '<a class="label" href="%s">%s</a>' % {
+                                       ds.build_url("admin/services/shadowsocks-libev/servers", v), fv}
+                       end
+                       line = n .. ": " .. fv
+                       table.insert(lines, line)
+               end
+       end
+end
+
+function option_install_package(s, tab)
+       local bin = s.sectiontype:gsub("_", "-", 1)
+       local installed = nixio.fs.access("/usr/bin/" .. bin)
+       if installed then
+               return
+       end
+       local opkg_package = "shadowsocks-libev-" .. bin
+       local p_install
+       if tab then
+               p_install = s:taboption(tab, Button, "_install")
+       else
+               p_install = s:option(Button, "_install")
+       end
+       p_install.title      = translate("Package is not installed")
+       p_install.inputtitle = translate("Install package %q" % opkg_package)
+       p_install.inputstyle = "apply"
+
+       function p_install.write()
+               return luci.http.redirect(
+                       luci.dispatcher.build_url("admin/system/packages") ..
+                       "?submit=1&install=%s" % opkg_package
+               )
+       end
+end
+
+names_options_server = {
+       "server",
+       "server_port",
+       "method",
+       "key",
+       "password",
+}
+
+names_options_client = {
+       "server",
+       "local_address",
+       "local_port",
+}
+
+names_options_common = {
+       "verbose",
+       "fast_open",
+       "reuse_port",
+       "mode",
+       "mtu",
+       "timeout",
+       "user",
+}
+
+modes = {
+       "tcp_only",
+       "tcp_and_udp",
+       "udp_only",
+}
+
+actions = {
+       "bypass",
+       "forward",
+       "checkdst",
+}
+
+methods = {
+       -- aead
+       "aes-128-gcm",
+       "aes-192-gcm",
+       "aes-256-gcm",
+       -- stream
+       "table",
+       "rc4",
+       "rc4-md5",
+       "aes-128-cfb",
+       "aes-192-cfb",
+       "aes-256-cfb",
+       "aes-128-ctr",
+       "aes-192-ctr",
+       "aes-256-ctr",
+       "bf-cfb",
+       "camellia-128-cfb",
+       "camellia-192-cfb",
+       "camellia-256-cfb",
+       "salsa20",
+       "chacha20",
+       "chacha20-ietf",
+       "aes-128-gcm",
+       "aes-192-gcm",
+       "aes-256-gcm",
+}
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm b/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm
new file mode 100644 (file)
index 0000000..67e1c4f
--- /dev/null
@@ -0,0 +1,22 @@
+<div class="cbi-section-create cbi-tblsection-create">
+       <br />
+       <table class="cbi-section-table">
+               <tr class="cbi-section-table-row">
+                       <td class="cbi-section-table-cell" style="width:140px">
+                               <select class="cbi-input-select" id="_newinst.type" name="_newinst.type">
+                                       <option value="_dummy">-- instance type --</option>
+                                       <option value="ss_local">ss-local</option>
+                                       <option value="ss_tunnel">ss-tunnel</option>
+                                       <option value="ss_redir">ss-redir</option>
+                                       <option value="ss_server">ss-server</option>
+                               </select>
+                       </td>
+                       <td class="cbi-section-table-cell" style="width:110px">
+                               <input type="text" class="cbi-input-text" id="_newinst.name" name="_newinst.name" placeholder="<%:Name%>"/>
+                       </td>
+                       <td class="cbi-section-table-cell left">
+                               <input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>" value="<%:Add%>" />
+                       </td>
+               </tr>
+       </table>
+</div>
diff --git a/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po
deleted file mode 100644 (file)
index f2b18e3..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-msgid ""
-msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
-"Project-Id-Version: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Language-Team: \n"
-"MIME-Version: 1.0\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.8.11\n"
-"Last-Translator: Luiz Angelo Daros de Luca <luizluca@gmail.com>\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"Language: pt_BR\n"
-
-msgid "Access Control"
-msgstr "Controle de Acesso"
-
-msgid "Allow all except listed"
-msgstr "Permitir todos, exceto os listados"
-
-msgid "Allow listed only"
-msgstr "Permitir somente os listados"
-
-msgid "Bypassed IP"
-msgstr "Endereços IP Ignorados"
-
-msgid "Connection Timeout"
-msgstr "Tempo limite de conexão"
-
-msgid "Custom"
-msgstr "Personalizado"
-
-msgid "Disabled"
-msgstr "Desabilitado"
-
-msgid "Enable"
-msgstr "Ativar"
-
-msgid "Enabled"
-msgstr "Ativado"
-
-msgid "Encrypt Method"
-msgstr "Método de Cifragem"
-
-msgid "Forwarded IP"
-msgstr "Endereço IP Encaminhado"
-
-msgid "Forwarding Tunnel"
-msgstr "Tunel para Encaminhamento"
-
-msgid "Global Setting"
-msgstr "Opções Globais"
-
-msgid "Ignore List"
-msgstr "Lista de Ignorados"
-
-msgid "LAN"
-msgstr "LAN"
-
-msgid "LAN IP List"
-msgstr "Lista de endereços IP da LAN"
-
-msgid "Local Port"
-msgstr "Porta Local"
-
-msgid "Password"
-msgstr "Senha"
-
-msgid "Relay Mode"
-msgstr "Modo de Retransmissor"
-
-msgid "Server Address"
-msgstr "Endereço do Servidor"
-
-msgid "Server Port"
-msgstr "Porta do servidor"
-
-msgid "ShadowSocks-libev"
-msgstr "ShadowSocks-libev"
-
-msgid "ShadowSocks-libev is not running"
-msgstr "O serviço ShadowSocks-libev está parado"
-
-msgid "ShadowSocks-libev is running"
-msgstr "O serviço ShadowSocks-libev está em execução."
-
-msgid "UDP Forward"
-msgstr "Encaminhamento UDP"
-
-msgid "UDP Local Port"
-msgstr "Porta Local UDP"
-
-msgid "UDP Relay"
-msgstr "Retransmissão UDP"
-
-msgid "WAN"
-msgstr "WAN"
diff --git a/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po
deleted file mode 100644 (file)
index b0cf6d3..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-msgid ""
-msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
-"Project-Id-Version: PACKAGE VERSION\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
-"Language: sv\n"
-"MIME-Version: 1.0\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-msgid "Access Control"
-msgstr ""
-
-msgid "Allow all except listed"
-msgstr ""
-
-msgid "Allow listed only"
-msgstr ""
-
-msgid "Bypassed IP"
-msgstr ""
-
-msgid "Connection Timeout"
-msgstr ""
-
-msgid "Custom"
-msgstr ""
-
-msgid "Disabled"
-msgstr ""
-
-msgid "Enable"
-msgstr ""
-
-msgid "Enabled"
-msgstr ""
-
-msgid "Encrypt Method"
-msgstr ""
-
-msgid "Forwarded IP"
-msgstr ""
-
-msgid "Forwarding Tunnel"
-msgstr ""
-
-msgid "Global Setting"
-msgstr ""
-
-msgid "Ignore List"
-msgstr ""
-
-msgid "LAN"
-msgstr ""
-
-msgid "LAN IP List"
-msgstr ""
-
-msgid "Local Port"
-msgstr ""
-
-msgid "Password"
-msgstr ""
-
-msgid "Relay Mode"
-msgstr ""
-
-msgid "Server Address"
-msgstr ""
-
-msgid "Server Port"
-msgstr ""
-
-msgid "ShadowSocks-libev"
-msgstr ""
-
-msgid "ShadowSocks-libev is not running"
-msgstr ""
-
-msgid "ShadowSocks-libev is running"
-msgstr ""
-
-msgid "UDP Forward"
-msgstr ""
-
-msgid "UDP Local Port"
-msgstr ""
-
-msgid "UDP Relay"
-msgstr ""
-
-msgid "WAN"
-msgstr ""
-
-#~ msgid "Broadcast on all interfaces"
-#~ msgstr "Sänd i alla gränssnitt"
-
-#~ msgid "Choose the host to wake up or enter a custom MAC address to use"
-#~ msgstr ""
-#~ "Välj värden som ska väckas upp eller fyll i en anpassad MAC-adress att "
-#~ "använda"
-
-#~ msgid "Host to wake up"
-#~ msgstr "Värd som ska väckas upp"
-
-#~ msgid "Network interface to use"
-#~ msgstr "Nätverksgränssnitt som ska användas"
-
-#~ msgid ""
-#~ "Sometimes only one of the two tools works. If one fails, try the other one"
-#~ msgstr ""
-#~ "Ibland så fungerar bara en av de två verktygen. Prova med den andra om "
-#~ "den första misslyckades"
-
-#~ msgid "Specifies the interface the WoL packet is sent on"
-#~ msgstr "Anger gränssnittet som fjärrstartspaketet skickas med"
-
-#~ msgid "Starting WoL utility:"
-#~ msgstr "Startar hjälpprogrammet för fjärrstyrning av uppstart:"
-
-#~ msgid "Wake on LAN"
-#~ msgstr "Fjärrstyrning av uppstart"
-
-#~ msgid ""
-#~ "Wake on LAN is a mechanism to remotely boot computers in the local "
-#~ "network."
-#~ msgstr ""
-#~ "Fjärrstyrning av uppstart är en mekanism för att starta upp datorer via "
-#~ "fjärrstyrning i det lokala nätverket."
-
-#~ msgid "Wake up host"
-#~ msgstr "Väck upp värden"
-
-#~ msgid "WoL program"
-#~ msgstr "Program för fjärrstart"
diff --git a/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot b/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot
deleted file mode 100644 (file)
index 81bbcb7..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
-
-msgid "Access Control"
-msgstr ""
-
-msgid "Allow all except listed"
-msgstr ""
-
-msgid "Allow listed only"
-msgstr ""
-
-msgid "Bypassed IP"
-msgstr ""
-
-msgid "Connection Timeout"
-msgstr ""
-
-msgid "Custom"
-msgstr ""
-
-msgid "Disabled"
-msgstr ""
-
-msgid "Enable"
-msgstr ""
-
-msgid "Enabled"
-msgstr ""
-
-msgid "Encrypt Method"
-msgstr ""
-
-msgid "Forwarded IP"
-msgstr ""
-
-msgid "Forwarding Tunnel"
-msgstr ""
-
-msgid "Global Setting"
-msgstr ""
-
-msgid "Ignore List"
-msgstr ""
-
-msgid "LAN"
-msgstr ""
-
-msgid "LAN IP List"
-msgstr ""
-
-msgid "Local Port"
-msgstr ""
-
-msgid "Password"
-msgstr ""
-
-msgid "Relay Mode"
-msgstr ""
-
-msgid "Server Address"
-msgstr ""
-
-msgid "Server Port"
-msgstr ""
-
-msgid "ShadowSocks-libev"
-msgstr ""
-
-msgid "ShadowSocks-libev is not running"
-msgstr ""
-
-msgid "ShadowSocks-libev is running"
-msgstr ""
-
-msgid "UDP Forward"
-msgstr ""
-
-msgid "UDP Local Port"
-msgstr ""
-
-msgid "UDP Relay"
-msgstr ""
-
-msgid "WAN"
-msgstr ""
diff --git a/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po
deleted file mode 100644 (file)
index f86eee7..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-11-12 14:12+0800\n"
-"PO-Revision-Date: 2015-07-02 14:26+0800\n"
-"Last-Translator: Jian Chang <aa65535@live.com>\n"
-"Language: zh_CN\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Pootle 2.0.6\n"
-
-msgid "Access Control"
-msgstr "访问控制"
-
-msgid "Allow all except listed"
-msgstr "仅允许列表外"
-
-msgid "Allow listed only"
-msgstr "仅允许列表内"
-
-msgid "Bypassed IP"
-msgstr "被忽略的IP"
-
-msgid "Connection Timeout"
-msgstr "连接超时"
-
-msgid "Custom"
-msgstr "自定义"
-
-msgid "Disabled"
-msgstr "已禁用"
-
-msgid "Enable"
-msgstr "启用"
-
-msgid "Enabled"
-msgstr "已启用"
-
-msgid "Encrypt Method"
-msgstr "加密方式"
-
-msgid "Forwarded IP"
-msgstr "走代理的IP"
-
-msgid "Forwarding Tunnel"
-msgstr "UDP转发地址"
-
-msgid "Global Setting"
-msgstr "全局设置"
-
-msgid "Ignore List"
-msgstr "忽略列表"
-
-msgid "LAN"
-msgstr ""
-
-msgid "LAN IP List"
-msgstr "内网IP列表"
-
-msgid "Local Port"
-msgstr "本地端口"
-
-msgid "Password"
-msgstr "密码"
-
-msgid "Relay Mode"
-msgstr "中继模式"
-
-msgid "Server Address"
-msgstr "服务器地址"
-
-msgid "Server Port"
-msgstr "服务器端口"
-
-msgid "ShadowSocks-libev"
-msgstr "ShadowSocks-libev"
-
-msgid "ShadowSocks-libev is not running"
-msgstr "ShadowSocks-libev 未运行"
-
-msgid "ShadowSocks-libev is running"
-msgstr "ShadowSocks-libev 运行中"
-
-msgid "UDP Forward"
-msgstr "UDP转发"
-
-msgid "UDP Local Port"
-msgstr "UDP本地端口"
-
-msgid "UDP Relay"
-msgstr "UDP中继"
-
-msgid "WAN"
-msgstr ""
index 86382f6..71fb179 100644 (file)
@@ -5,8 +5,8 @@ module("luci.controller.travelmate", package.seeall)
 
 local fs = require("nixio.fs")
 local util = require("luci.util")
-local template = require("luci.template")
 local i18n = require("luci.i18n")
+local templ = require("luci.template")
 
 function index()
        if not nixio.fs.access("/etc/config/travelmate") then
@@ -14,15 +14,21 @@ function index()
        end
        entry({"admin", "services", "travelmate"}, firstchild(), _("Travelmate"), 40).dependent = false
        entry({"admin", "services", "travelmate", "tab_from_cbi"}, cbi("travelmate/overview_tab", {hideresetbtn=true, hidesavebtn=true}), _("Overview"), 10).leaf = true
-       entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 20).leaf = true
+       entry({"admin", "services", "travelmate", "stations"}, template("travelmate/stations"), _("Wireless Stations"), 20).leaf = true
+       entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 30).leaf = true
        entry({"admin", "services", "travelmate", "advanced"}, firstchild(), _("Advanced"), 100)
        entry({"admin", "services", "travelmate", "advanced", "configuration"}, cbi("travelmate/configuration_tab"), _("Edit Travelmate Configuration"), 110).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_wireless"}, cbi("travelmate/cfg_wireless_tab"), _("Edit Wireless Configuration"), 120).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_network"}, cbi("travelmate/cfg_network_tab"), _("Edit Network Configuration"), 130).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_firewall"}, cbi("travelmate/cfg_firewall_tab"), _("Edit Firewall Configuration"), 140).leaf = true
+
+       entry({"admin", "services", "travelmate", "wifiscan"}, template("travelmate/wifi_scan")).leaf = true
+       entry({"admin", "services", "travelmate", "wifiadd"}, cbi("travelmate/wifi_add", {hideresetbtn=true, hidesavebtn=true})).leaf = true
+       entry({"admin", "services", "travelmate", "wifiedit"}, cbi("travelmate/wifi_edit", {hideresetbtn=true, hidesavebtn=true})).leaf = true
+       entry({"admin", "services", "travelmate", "wifidelete"}, cbi("travelmate/wifi_delete", {hideresetbtn=true, hidesavebtn=true})).leaf = true
 end
 
 function logread()
        local logfile = util.trim(util.exec("logread -e 'travelmate'"))
-       template.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile})
+       templ.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile})
 end
index 236bbb0..64ab880 100644 (file)
@@ -2,12 +2,13 @@
 -- This is free software, licensed under the Apache License, Version 2.0
 
 local fs = require("nixio.fs")
-local uci = require("uci")
+local uci = require("luci.model.uci").cursor()
 local json = require("luci.jsonc")
 local nw  = require("luci.model.network").init()
 local fw  = require("luci.model.firewall").init()
-local uplink = uci.get("network", "trm_wwan") or ""
+local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
 local trminput = uci.get("travelmate", "global", "trm_rtfile") or "/tmp/trm_runtime.json"
+local uplink = uci.get("network", trmiface) or ""
 local parse = json.parse(fs.readfile(trminput) or "")
 
 m = Map("travelmate", translate("Travelmate"),
@@ -21,70 +22,81 @@ function m.on_after_commit(self)
        luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
 end
 
--- Main travelmate options
-
 s = m:section(NamedSection, "global", "travelmate")
 
-o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate"))
-o1.default = o1.disabled
-o1.rmempty = false
-
-o2 = s:option(Flag, "trm_automatic", translate("Enable 'automatic' mode"),
-       translate("Keep travelmate in an active state."))
-o2.default = o2.enabled
-o2.rmempty = false
+-- Interface Wizard
 
-o3 = s:option(Value, "trm_iface", translate("Restrict interface trigger to certain interface(s)"),
-       translate("Space separated list of interfaces that trigger travelmate processing. "..
-       "To disable event driven (re-)starts remove all entries."))
-o3.rmempty = true
+if uplink == "" then
+       dv = s:option(DummyValue, "nil", translate("Interface Wizard"))
+       dv.template = "cbi/nullsection"
 
-o4 = s:option(Value, "trm_triggerdelay", translate("Trigger delay"),
-       translate("Additional trigger delay in seconds before travelmate processing begins."))
-o4.default = 2
-o4.datatype = "range(1,90)"
-o4.rmempty = false
+       o = s:option(Value, "trm_iface", translate("Uplink interface"))
+       o.datatype = "and(uciname,rangelength(3,15))"
+       o.default = "trm_wwan"
+       o.rmempty = false
 
-o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging"))
-o5.default = o5.disabled
-o5.rmempty = false
+       function o.validate(self, value)
+               iface = value
+               return iface
+       end
 
--- Interface setup
+       function o.write(self, section, value)
+               uci:set("travelmate", section, "trm_iface", iface)
+               uci:save("travelmate")
+               uci:commit("travelmate")
+       end
 
-if uplink == "" then
-       dv = s:option(DummyValue, "_dummy", translate("Interface Setup"))
-       dv.template = "cbi/nullsection"
        btn = s:option(Button, "", translate("Create Uplink Interface"),
-               translate("Automatically create a new wireless wan uplink interface 'trm_wwan', configure it to use dhcp and ")
+               translate("Create a new wireless wan uplink interface, configure it to use dhcp and ")
                .. translate("add it to the wan zone of the firewall. This step has only to be done once."))
        btn.inputtitle = translate("Add Interface")
        btn.inputstyle = "apply"
        btn.disabled = false
        function btn.write()
-               local name = "trm_wwan"
-               local net = nw:add_network(name, { proto = "dhcp" })
+               local net = nw:add_network(iface, { proto = "dhcp" })
                if net then
                        nw:save("network")
                        nw:commit("network")
                        local zone = fw:get_zone_by_network("wan")
                        if zone then
-                               zone:add_network(name)
+                               zone:add_network(iface)
                                fw:save("firewall")
                                fw:commit("firewall")
+                               luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
                        end
-                       luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
-                       luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
                end
+               luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
        end
-else
-       dv = s:option(DummyValue, "_dummy", translate("Interface Setup"),
-               translate("<br />&nbsp;Network Interface 'trm_wwan' created successfully. ")
-               .. translatef("Scan &amp; Add new wireless stations via standard "
-               .. "<a href=\"%s\">"
-               .. "Wireless Setup</a>", luci.dispatcher.build_url("admin/network/wireless")))
-       dv.template = "cbi/nullsection"
+       return m
 end
 
+-- Main travelmate options
+
+o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate"))
+o1.default = o1.disabled
+o1.rmempty = false
+
+o2 = s:option(Flag, "trm_automatic", translate("Enable 'automatic' mode"),
+       translate("Keep travelmate in an active state."))
+o2.default = o2.enabled
+o2.rmempty = false
+
+o3 = s:option(Value, "trm_iface", translate("Uplink / Trigger interface"),
+       translate("Name of the uplink interface that triggers travelmate processing."))
+o3.datatype = "and(uciname,rangelength(3,15))"
+o3.default = "trm_wwan"
+o3.rmempty = false
+
+o4 = s:option(Value, "trm_triggerdelay", translate("Trigger delay"),
+       translate("Additional trigger delay in seconds before travelmate processing begins."))
+o4.default = 2
+o4.datatype = "range(1,90)"
+o4.rmempty = false
+
+o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging"))
+o5.default = o5.disabled
+o5.rmempty = false
+
 -- Runtime information
 
 ds = s:option(DummyValue, "_dummy", translate("Runtime information"))
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua
new file mode 100644 (file)
index 0000000..361027f
--- /dev/null
@@ -0,0 +1,65 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local fs = require("nixio.fs")
+local uci = require("luci.model.uci").cursor()
+local http = require("luci.http")
+local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
+
+m = SimpleForm("add", translate("Add Wireless Uplink Configuration"))
+m.cancel = translate("Back to overview")
+m.reset = false
+
+function m.on_cancel()
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+m.hidden = {
+       device      = http.formvalue("device"),
+       ssid        = http.formvalue("ssid"),
+       wep         = http.formvalue("wep"),
+       wpa_suites      = http.formvalue("wpa_suites"),
+       wpa_version = http.formvalue("wpa_version")
+}
+
+wssid = m:field(Value, "ssid", translate("SSID"))
+wssid.default = m.hidden.ssid
+
+if (tonumber(m.hidden.wep) or 0) == 1 then
+       wkey = m:field(Value, "key", translate("WEP passphrase"),
+               translate("Specify the secret encryption key here."))
+       wkey.password = true
+       wkey.datatype = "wepkey"
+elseif (tonumber(m.hidden.wpa_version) or 0) > 0 and
+       (m.hidden.wpa_suites == "PSK" or m.hidden.wpa_suites == "PSK2")
+then
+       wkey = m:field(Value, "key", translate("WPA passphrase"),
+               translate("Specify the secret encryption key here."))
+       wkey.password = true
+       wkey.datatype = "wpakey"
+end
+
+function wssid.write(self, section, value)
+       newsection = uci:section("wireless", "wifi-iface", nil, {
+               mode       = "sta",
+               network    = trmiface,
+               device     = m.hidden.device,
+               ssid       = wssid:formvalue(section),
+               disabled   = "1"
+       })
+       if (tonumber(m.hidden.wep) or 0) == 1 then
+               uci:set("wireless", newsection, "encryption", "wep-open")
+               uci:set("wireless", newsection, "key", "1")
+               uci:set("wireless", newsection, "key1", wkey:formvalue(section))
+       elseif (tonumber(m.hidden.wpa_version) or 0) > 0 then
+               uci:set("wireless", newsection, "encryption", "psk2")
+               uci:set("wireless", newsection, "key", wkey:formvalue(section))
+       else
+               uci:set("wireless", newsection, "encryption", "none")
+       end
+       uci:save("wireless")
+       uci:commit("wireless")
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+return m
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua
new file mode 100644 (file)
index 0000000..97ec1ca
--- /dev/null
@@ -0,0 +1,14 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local uci = require("luci.model.uci").cursor()
+local http = require("luci.http")
+local cfg = http.formvalue("cfg")
+
+if cfg ~= nil then
+       uci:delete("wireless", cfg)
+       uci:save("wireless")
+       uci:commit("wireless")
+end
+
+http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua
new file mode 100644 (file)
index 0000000..0bae984
--- /dev/null
@@ -0,0 +1,49 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local fs = require("nixio.fs")
+local uci = require("luci.model.uci").cursor()
+local http = require("luci.http")
+
+m = SimpleForm("edit", translate("Edit Wireless Uplink Configuration"))
+m.cancel = translate("Back to overview")
+m.reset = false
+
+function m.on_cancel()
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+m.hidden = {
+       cfg = http.formvalue("cfg")
+}
+
+local s = uci:get_all("wireless", m.hidden.cfg)
+if s ~= nil then
+       wssid = m:field(Value, "ssid", translate("SSID"))
+       wssid.default = s.ssid
+       
+       if s.encryption and s.key then
+               wkey = m:field(Value, "key", translatef("Passphrase (%s)", s.encryption))
+               wkey.password = true
+               wkey.default = s.key
+               if s.encryption == "wep" then
+                       wkey.datatype = "wepkey"
+               else
+                       wkey.datatype = "wpakey"
+               end
+       end
+else
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+function wssid.write(self, section, value)
+       uci:set("wireless", m.hidden.cfg, "ssid", wssid:formvalue(section))
+       if s.encryption and s.key then
+               uci:set("wireless", m.hidden.cfg, "key", wkey:formvalue(section))
+       end
+       uci:save("wireless")
+       uci:commit("wireless")
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+return m
index ee3a455..2b98855 100644 (file)
@@ -5,6 +5,6 @@ This is free software, licensed under the Apache License, Version 2.0
 
 <%+cbi/valueheader%>
 
-<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="border: none; box-shadow: none; background-color: #ffffff; color: #0069d6;" value="<%=self:cfgvalue(section)%>" disabled="disabled" />
+<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="border:none; box-shadow:none; background-color:#ffffff; color:#0069d6;" value="<%=self:cfgvalue(section)%>" disabled="disabled" />
 
 <%+cbi/valuefooter%>
diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm
new file mode 100644 (file)
index 0000000..e79fb0c
--- /dev/null
@@ -0,0 +1,73 @@
+<%#
+Copyright 2017 Dirk Brenken (dev@brenken.org)
+This is free software, licensed under the Apache License, Version 2.0
+-%>
+
+
+<%-
+  local write = io.write
+  local uci = require "luci.model.uci".cursor()
+  local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
+-%>
+
+<%+header%>
+
+<div class="cbi-map">
+<h2 name="content"><%:Wireless Stations%></h2>
+<div class="cbi-map-descr"><%:Provides an overview of all configured uplink interfaces for travelmate. You can edit and delete existing interfaces or scan for new uplinks.%></div>
+
+<fieldset class="cbi-section">
+  <table class="cbi-section-table" style="empty-cells:hide">
+    <tr class="cbi-section-table-titles">
+      <th class="cbi-section-table-cell" style="text-align:left"><%:Device%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:Mode%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink Interface%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:SSID%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
+      <th class="cbi-section-table-cell" style="text-align:left" colspan="2"><%:Disabled%></th>
+    </tr>
+<%
+  uci:foreach("wireless", "wifi-iface", function(s)
+    local section = s['.name']
+    local device = s.device or ""
+    local mode = s.mode or ""
+    local iface = s.network or ""
+    local ssid = s.ssid or ""
+    local encryption = s.encryption or ""
+    local disabled = s.disabled or ""
+    if iface == trmiface then
+%>
+    <tr class="cbi-section-table-row cbi-rowstyle-6"> 
+      <td style="text-align:left"><%=device%></td>
+      <td style="text-align:left"><%=mode%></td>
+      <td style="text-align:left"><%=iface%></td>
+      <td style="text-align:left"><%=ssid%></td>
+      <td style="text-align:left"><%=encryption%></td>
+      <td style="text-align:left"><%=disabled%></td>
+      <td class="cbi-value-field" style="width:200px;text-align:right">
+        <input type="button" class="cbi-button cbi-button-edit" style="width:85px" onclick="location.href='<%=url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" value="<%:Edit%>" />
+        <input type="button" class="cbi-button cbi-button-remove" style="width:85px" onclick="location.href='<%=url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Delete this Uplink%>" value="<%:Delete%>"/>
+      </td>
+    </tr>
+<% 
+    end
+  end)
+%>
+  </table>
+</fieldset>
+<div class="cbi-page-actions right">
+<%
+  uci:foreach("wireless", "wifi-device", function(s)
+    local device = s[".name"]
+%>
+  <form class="inline" action="<%=url('admin/services/travelmate/wifiscan')%>" method="post">
+    <input type="hidden" name="device" value="<%=device%>" />
+    <input type="hidden" name="token" value="<%=token%>" />
+    <input type="submit" class="cbi-button cbi-button-find" style="width:110px" title="<%:Find and join network on %><%=device%>" value="<%:Scan %><%=device%>" />
+  </form>
+<%
+  end)
+%>
+</div>
+
+<%+footer%>
diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm
new file mode 100644 (file)
index 0000000..e1818a0
--- /dev/null
@@ -0,0 +1,108 @@
+<%#
+Copyright 2017 Dirk Brenken (dev@brenken.org)
+This is free software, licensed under the Apache License, Version 2.0
+-%>
+
+<%-
+    local sys = require "luci.sys"
+    local utl = require "luci.util"
+    local dev = luci.http.formvalue("device")
+    local iw = luci.sys.wifi.getiwinfo(dev)
+
+    if not iw then
+        luci.http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+    end
+
+    function format_wifi_encryption(info)
+        if info.wep == true then
+            return translate("WEP")
+        elseif info.wpa > 0 then
+            return translate("WPA / WPA2")
+        elseif info.enabled then
+            return translate("Unknown")
+        else
+            return translate("Open")
+        end
+    end
+
+    function percent_wifi_signal(info)
+        local qc = info.quality or 0
+        local qm = info.quality_max or 0
+        if info.bssid and qc > 0 and qm > 0 then
+            return math.floor((100 / qm) * qc)
+        else
+            return 0
+        end
+    end
+
+    function scanlist(times)
+        local i, k, v
+        local l = { }
+        local s = { }
+
+        for i = 1, times do
+            for k, v in ipairs(iw.scanlist or { }) do
+                if not s[v.bssid] then
+                    l[#l+1] = v
+                    s[v.bssid] = true
+                end
+            end
+        end
+        return l
+    end
+-%>
+
+<%+header%>
+
+<div class="cbi-map">
+<h2 name="content"><%:Wireless Scan%></h2>
+
+    <fieldset class="cbi-section">
+        <table class="cbi-section-table" style="empty-cells:hide">
+            <tr class="cbi-section-table-titles">
+                <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink SSID%></th>
+                <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
+                <th class="cbi-section-table-cell" style="text-align:left" colspan="2"><%:Signal strength%></th>
+            </tr>
+            <% for i, net in ipairs(scanlist(3)) do net.encryption = net.encryption or { } %>
+            <tr class="cbi-section-table-row cbi-rowstyle-4">
+                <td class="cbi-value-field" style="text-align:left">
+                    <strong><%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%></strong>
+                </td>
+                <td class="cbi-value-field" style="text-align:left">
+                    <%=format_wifi_encryption(net.encryption)%>
+                </td>
+                <td class="cbi-value-field" style="text-align:left">
+                    <%=percent_wifi_signal(net)%> %
+                </td>
+                <td class="cbi-value-field" style="width:120px; text-align:right">
+                    <form class="inline" action="<%=url('admin/services/travelmate/wifiadd')%>" method="post">
+                        <input type="hidden" name="token" value="<%=token%>" />
+                        <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>" />
+                        <input type="hidden" name="ssid" value="<%=utl.pcdata(net.ssid)%>" />
+                        <input type="hidden" name="wep" value="<%=net.encryption.wep and 1 or 0%>" />
+                        <% if net.encryption.wpa then %>
+                        <input type="hidden" name="wpa_version" value="<%=net.encryption.wpa%>" />
+                        <% for _, v in ipairs(net.encryption.auth_suites) do %><input type="hidden" name="wpa_suites" value="<%=v%>" />
+                        <% end; for _, v in ipairs(net.encryption.group_ciphers) do %><input type="hidden" name="wpa_group" value="<%=v%>" />
+                        <% end; for _, v in ipairs(net.encryption.pair_ciphers) do %><input type="hidden" name="wpa_pairwise" value="<%=v%>" />
+                        <% end; end %>
+                        <input class="cbi-button cbi-button-apply" style="width:110px" type="submit" value="<%:Add Uplink%>" />
+                    </form>
+                </td>
+            </tr>
+            <% end %>
+        </table>
+    </fieldset>
+<div class="cbi-page-actions right">
+    <form class="inline" action="<%=url('admin/services/travelmate/stations')%>" method="post">
+        <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>" />
+    </form>
+    <form class="inline" action="<%=url('admin/services/travelmate/wifiscan')%>" method="post">
+        <input type="hidden" name="token" value="<%=token%>" />
+        <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>" />
+        <input class="cbi-button cbi-input-find" type="submit" value="<%:Repeat scan%>" />
+    </form>
+</div>
+
+<%+footer%>
index 8e66cbc..4be917d 100644 (file)
@@ -727,7 +727,7 @@ function cbi_filebrowser(id, defpath) {
        browser.focus();
 }
 
-function cbi_browser_init(id, defpath)
+function cbi_browser_init(id, resource, defpath)
 {
        function cbi_browser_btnclick(e) {
                cbi_filebrowser(id, defpath);
@@ -738,7 +738,7 @@ function cbi_browser_init(id, defpath)
 
        var btn = document.createElement('img');
        btn.className = 'cbi-image-button';
-       btn.src = cbi_strings.path.resource + '/cbi/folder.gif';
+       btn.src = (resource || cbi_strings.path.resource) + '/cbi/folder.gif';
        field.parentNode.insertBefore(btn, field.nextSibling);
 
        cbi_bind(btn, 'click', cbi_browser_btnclick);
@@ -805,7 +805,7 @@ function cbi_dynlist_init(parent, datatype, optional, choices)
                        parent.appendChild(b);
                        if (datatype == 'file')
                        {
-                               cbi_browser_init(t.id, parent.getAttribute('data-browser-path'));
+                               cbi_browser_init(t.id, null, parent.getAttribute('data-browser-path'));
                        }
 
                        parent.appendChild(document.createElement('br'));
index 7f19c28..b38cce9 100644 (file)
@@ -3019,7 +3019,7 @@ msgid "Switch VLAN"
 msgstr "交换机 VLAN"
 
 msgid "Switch protocol"
-msgstr "交换机协议"
+msgstr "切换协议"
 
 msgid "Sync with browser"
 msgstr "同步浏览器时间"