summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-base/luasrc
diff options
context:
space:
mode:
Diffstat (limited to 'modules/luci-base/luasrc')
-rw-r--r--modules/luci-base/luasrc/cbi.lua172
-rw-r--r--modules/luci-base/luasrc/cbi/datatypes.lua78
-rw-r--r--modules/luci-base/luasrc/controller/admin/servicectl.lua2
-rw-r--r--modules/luci-base/luasrc/dispatcher.lua146
-rw-r--r--modules/luci-base/luasrc/http.lua1
-rw-r--r--modules/luci-base/luasrc/http/protocol.lua2
-rw-r--r--modules/luci-base/luasrc/model/ipkg.lua24
-rw-r--r--modules/luci-base/luasrc/model/network.lua305
-rw-r--r--modules/luci-base/luasrc/model/uci.luadoc8
-rw-r--r--modules/luci-base/luasrc/sys.lua131
-rw-r--r--modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua5
-rw-r--r--modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua2
-rw-r--r--modules/luci-base/luasrc/util.lua75
-rw-r--r--modules/luci-base/luasrc/view/cbi/apply_xhr.htm4
-rw-r--r--modules/luci-base/luasrc/view/cbi/browser.htm2
-rw-r--r--modules/luci-base/luasrc/view/cbi/dynlist.htm4
-rw-r--r--modules/luci-base/luasrc/view/cbi/error.htm2
-rw-r--r--modules/luci-base/luasrc/view/cbi/header.htm1
-rw-r--r--modules/luci-base/luasrc/view/cbi/map.htm2
-rw-r--r--modules/luci-base/luasrc/view/cbi/mvalue.htm2
-rw-r--r--modules/luci-base/luasrc/view/cbi/simpleform.htm3
-rw-r--r--modules/luci-base/luasrc/view/cbi/upload.htm16
-rw-r--r--modules/luci-base/luasrc/view/cbi/value.htm5
-rw-r--r--modules/luci-base/luasrc/view/csrftoken.htm24
-rw-r--r--modules/luci-base/luasrc/view/error404.htm2
-rw-r--r--modules/luci-base/luasrc/view/error500.htm2
-rw-r--r--modules/luci-base/luasrc/view/sysauth.htm2
27 files changed, 626 insertions, 396 deletions
diff --git a/modules/luci-base/luasrc/cbi.lua b/modules/luci-base/luasrc/cbi.lua
index f3d4618b65..2c1bb4d226 100644
--- a/modules/luci-base/luasrc/cbi.lua
+++ b/modules/luci-base/luasrc/cbi.lua
@@ -38,7 +38,7 @@ function load(cbimap, ...)
require("luci.config")
require("luci.util")
- local upldir = "/lib/uci/upload/"
+ local upldir = "/etc/luci-uploads/"
local cbidir = luci.util.libpath() .. "/model/cbi/"
local func, err
@@ -367,63 +367,64 @@ end
-- Use optimized UCI writing
function Map.parse(self, readinput, ...)
- self.readinput = (readinput ~= false)
- self:_run_hooks("on_parse")
-
if self:formvalue("cbi.skip") then
self.state = FORM_SKIP
+ elseif not self.save then
+ self.state = FORM_INVALID
+ elseif not self:submitstate() then
+ self.state = FORM_NODATA
+ end
+
+ -- Back out early to prevent unauthorized changes on the subsequent parse
+ if self.state ~= nil then
return self:state_handler(self.state)
end
+ self.readinput = (readinput ~= false)
+ self:_run_hooks("on_parse")
+
Node.parse(self, ...)
- if self.save then
- self:_run_hooks("on_save", "on_before_save")
+ self:_run_hooks("on_save", "on_before_save")
+ for i, config in ipairs(self.parsechain) do
+ self.uci:save(config)
+ end
+ self:_run_hooks("on_after_save")
+ if (not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply") then
+ self:_run_hooks("on_before_commit")
for i, config in ipairs(self.parsechain) do
- self.uci:save(config)
- end
- self:_run_hooks("on_after_save")
- if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
- self:_run_hooks("on_before_commit")
- for i, config in ipairs(self.parsechain) do
- self.uci:commit(config)
-
- -- Refresh data because commit changes section names
- self.uci:load(config)
- end
- self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
- if self.apply_on_parse then
- self.uci:apply(self.parsechain)
- self:_run_hooks("on_apply", "on_after_apply")
- else
- -- This is evaluated by the dispatcher and delegated to the
- -- template which in turn fires XHR to perform the actual
- -- apply actions.
- self.apply_needed = true
- end
-
- -- Reparse sections
- Node.parse(self, true)
+ self.uci:commit(config)
+ -- Refresh data because commit changes section names
+ self.uci:load(config)
end
- for i, config in ipairs(self.parsechain) do
- self.uci:unload(config)
- end
- if type(self.commit_handler) == "function" then
- self:commit_handler(self:submitstate())
+ self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
+ if self.apply_on_parse then
+ self.uci:apply(self.parsechain)
+ self:_run_hooks("on_apply", "on_after_apply")
+ else
+ -- This is evaluated by the dispatcher and delegated to the
+ -- template which in turn fires XHR to perform the actual
+ -- apply actions.
+ self.apply_needed = true
end
+
+ -- Reparse sections
+ Node.parse(self, true)
+ end
+ for i, config in ipairs(self.parsechain) do
+ self.uci:unload(config)
+ end
+ if type(self.commit_handler) == "function" then
+ self:commit_handler(self:submitstate())
end
- if self:submitstate() then
- if not self.save then
- self.state = FORM_INVALID
- elseif self.proceed then
- self.state = FORM_PROCEED
- else
- self.state = self.changed and FORM_CHANGED or FORM_VALID
- end
+ if self.proceed then
+ self.state = FORM_PROCEED
+ elseif self.changed then
+ self.state = FORM_CHANGED
else
- self.state = FORM_NODATA
+ self.state = FORM_VALID
end
return self:state_handler(self.state)
@@ -1470,6 +1471,7 @@ function Value.__init__(self, ...)
self.template = "cbi/value"
self.keylist = {}
self.vallist = {}
+ self.readonly = nil
end
function Value.reset_values(self)
@@ -1483,6 +1485,10 @@ function Value.value(self, key, val)
table.insert(self.vallist, tostring(val))
end
+function Value.parse(self, section, novld)
+ if self.readonly then return end
+ AbstractValue.parse(self, section, novld)
+end
-- DummyValue - This does nothing except being there
DummyValue = class(AbstractValue)
@@ -1527,17 +1533,25 @@ function Flag.__init__(self, ...)
end
-- A flag can only have two states: set or unset
-function Flag.parse(self, section)
+function Flag.parse(self, section, novld)
local fexists = self.map:formvalue(
FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
if fexists then
local fvalue = self:formvalue(section) and self.enabled or self.disabled
local cvalue = self:cfgvalue(section)
- if fvalue ~= self.default or (not self.optional and not self.rmempty) then
- self:write(section, fvalue)
- else
+ local val_err
+ fvalue, val_err = self:validate(fvalue, section)
+ if not fvalue then
+ if not novld then
+ self:add_error(section, "invalid", val_err)
+ end
+ return
+ end
+ if fvalue == self.default and (self.optional or self.rmempty) then
self:remove(section)
+ else
+ self:write(section, fvalue)
end
if (fvalue ~= cvalue) then self.section.changed = true end
else
@@ -1549,7 +1563,9 @@ end
function Flag.cfgvalue(self, section)
return AbstractValue.cfgvalue(self, section) or self.default
end
-
+function Flag.validate(self, value)
+ return value
+end
--[[
ListValue - A one-line value predefined in a list
@@ -1795,6 +1811,7 @@ function Button.__init__(self, ...)
self.template = "cbi/button"
self.inputstyle = nil
self.rmempty = true
+ self.unsafeupload = false
end
@@ -1811,9 +1828,15 @@ function FileUpload.__init__(self, ...)
end
function FileUpload.formcreated(self, section)
- return AbstractValue.formcreated(self, section) or
- self.map:formvalue("cbi.rlf."..section.."."..self.option) or
- self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
+ if self.unsafeupload then
+ return AbstractValue.formcreated(self, section) or
+ self.map:formvalue("cbi.rlf."..section.."."..self.option) or
+ self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") or
+ self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
+ else
+ return AbstractValue.formcreated(self, section) or
+ self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
+ end
end
function FileUpload.cfgvalue(self, section)
@@ -1824,27 +1847,50 @@ function FileUpload.cfgvalue(self, section)
return nil
end
+-- If we have a new value, use it
+-- otherwise use old value
+-- deletion should be managed by a separate button object
+-- unless self.unsafeupload is set in which case if the user
+-- choose to remove the old file we do so.
+-- Also, allow to specify (via textbox) a file already on router
function FileUpload.formvalue(self, section)
local val = AbstractValue.formvalue(self, section)
if val then
- if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
- not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
- then
+ if self.unsafeupload then
+ if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
+ not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
+ then
+ return val
+ end
+ fs.unlink(val)
+ self.value = nil
+ return nil
+ elseif val ~= "" then
return val
- end
- fs.unlink(val)
- self.value = nil
+ end
end
- return nil
+ val = luci.http.formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
+ if val == "" then
+ val = nil
+ end
+ if not self.unsafeupload then
+ if not val then
+ val = self.map:formvalue("cbi.rlf."..section.."."..self.option)
+ end
+ end
+ return val
end
function FileUpload.remove(self, section)
- local val = AbstractValue.formvalue(self, section)
- if val and fs.access(val) then fs.unlink(val) end
- return AbstractValue.remove(self, section)
+ if self.unsafeupload then
+ local val = AbstractValue.formvalue(self, section)
+ if val and fs.access(val) then fs.unlink(val) end
+ return AbstractValue.remove(self, section)
+ else
+ return nil
+ end
end
-
FileBrowser = class(AbstractValue)
function FileBrowser.__init__(self, ...)
diff --git a/modules/luci-base/luasrc/cbi/datatypes.lua b/modules/luci-base/luasrc/cbi/datatypes.lua
index ebd7e594f7..626ad91c75 100644
--- a/modules/luci-base/luasrc/cbi/datatypes.lua
+++ b/modules/luci-base/luasrc/cbi/datatypes.lua
@@ -176,14 +176,43 @@ function hostname(val)
return false
end
-function host(val)
- return hostname(val) or ipaddr(val)
+function host(val, ipv4only)
+ return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
end
function network(val)
return uciname(val) or host(val)
end
+function hostport(val, ipv4only)
+ local h, p = val:match("^([^:]+):([^:]+)$")
+ return not not (h and p and host(h, ipv4only) and port(p))
+end
+
+function ip4addrport(val, bracket)
+ local h, p = val:match("^([^:]+):([^:]+)$")
+ return (h and p and ip4addr(h) and port(p))
+end
+
+function ip4addrport(val)
+ local h, p = val:match("^([^:]+):([^:]+)$")
+ return (h and p and ip4addr(h) and port(p))
+end
+
+function ipaddrport(val, bracket)
+ local h, p = val:match("^([^%[%]:]+):([^:]+)$")
+ if (h and p and ip4addr(h) and port(p)) then
+ return true
+ elseif (bracket == 1) then
+ h, p = val:match("^%[(.+)%]:([^:]+)$")
+ if (h and p and ip6addr(h) and port(p)) then
+ return true
+ end
+ end
+ h, p = val:match("^([^%[%]]+):([^:]+)$")
+ return (h and p and ip6addr(h) and port(p))
+end
+
function wpakey(val)
if #val == 64 then
return (val:match("^[a-fA-F0-9]+$") ~= nil)
@@ -331,3 +360,48 @@ end
function phonedigit(val)
return (val:match("^[0-9\*#!%.]+$") ~= nil)
end
+
+function timehhmmss(val)
+ return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
+end
+
+function dateyyyymmdd(val)
+ if val ~= nil then
+ yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
+ if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
+ return false;
+ end
+ year = tonumber(yearstr)
+ month = tonumber(monthstr)
+ day = tonumber(daystr)
+ if (year == nil) or (month == nil) or (day == nil) then
+ return false;
+ end
+
+ local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+
+ local function is_leap_year(year)
+ return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
+ end
+
+ function get_days_in_month(month, year)
+ if (month == 2) and is_leap_year(year) then
+ return 29
+ else
+ return days_in_month[month]
+ end
+ end
+ if (year < 2015) then
+ return false
+ end
+ if ((month == 0) or (month > 12)) then
+ return false
+ end
+ if ((day == 0) or (day > get_days_in_month(month, year))) then
+ return false
+ end
+ return true
+ end
+ return false
+end
+
diff --git a/modules/luci-base/luasrc/controller/admin/servicectl.lua b/modules/luci-base/luasrc/controller/admin/servicectl.lua
index 5b855cb24b..1d73eb4ecc 100644
--- a/modules/luci-base/luasrc/controller/admin/servicectl.lua
+++ b/modules/luci-base/luasrc/controller/admin/servicectl.lua
@@ -6,7 +6,7 @@ module("luci.controller.admin.servicectl", package.seeall)
function index()
entry({"servicectl"}, alias("servicectl", "status")).sysauth = "root"
entry({"servicectl", "status"}, call("action_status")).leaf = true
- entry({"servicectl", "restart"}, call("action_restart")).leaf = true
+ entry({"servicectl", "restart"}, post("action_restart")).leaf = true
end
function action_status()
diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua
index 8b8d1fa349..cd5d77a12b 100644
--- a/modules/luci-base/luasrc/dispatcher.lua
+++ b/modules/luci-base/luasrc/dispatcher.lua
@@ -1,4 +1,5 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
local fs = require "nixio.fs"
@@ -26,14 +27,6 @@ function build_url(...)
local path = {...}
local url = { http.getenv("SCRIPT_NAME") or "" }
- local k, v
- for k, v in pairs(context.urltoken) do
- url[#url+1] = "/;"
- url[#url+1] = http.urlencode(k)
- url[#url+1] = "="
- url[#url+1] = http.urlencode(v)
- end
-
local p
for _, p in ipairs(path) do
if p:match("^[a-zA-Z0-9_%-%.%%/,;]+$") then
@@ -42,6 +35,10 @@ function build_url(...)
end
end
+ if #path == 0 then
+ url[#url+1] = "/"
+ end
+
return table.concat(url, "")
end
@@ -112,24 +109,11 @@ function authenticator.htmlauth(validator, accs, default)
return user
end
- if context.urltoken.stok then
- context.urltoken.stok = nil
-
- local cookie = 'sysauth=%s; expires=%s; path=%s/' %{
- http.getcookie('sysauth') or 'x',
- 'Thu, 01 Jan 1970 01:00:00 GMT',
- build_url()
- }
-
- http.header("Set-Cookie", cookie)
- http.redirect(build_url())
- else
- require("luci.i18n")
- require("luci.template")
- context.path = {}
- http.status(403, "Forbidden")
- luci.template.render("sysauth", {duser=default, fuser=user})
- end
+ require("luci.i18n")
+ require("luci.template")
+ context.path = {}
+ http.status(403, "Forbidden")
+ luci.template.render("sysauth", {duser=default, fuser=user})
return false
@@ -140,7 +124,6 @@ function httpdispatch(request, prefix)
local r = {}
context.request = r
- context.urltoken = {}
local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
@@ -150,18 +133,8 @@ function httpdispatch(request, prefix)
end
end
- local tokensok = true
for node in pathinfo:gmatch("[^/]+") do
- local tkey, tval
- if tokensok then
- tkey, tval = node:match(";(%w+)=([a-fA-F0-9]*)")
- end
- if tkey then
- context.urltoken[tkey] = tval
- else
- tokensok = false
- r[#r+1] = node
- end
+ r[#r+1] = node
end
local stat, err = util.coxpcall(function()
@@ -173,6 +146,48 @@ function httpdispatch(request, prefix)
--context._disable_memtrace()
end
+local function require_post_security(target)
+ if type(target) == "table" then
+ if type(target.post) == "table" then
+ local param_name, required_val, request_val
+
+ for param_name, required_val in pairs(target.post) do
+ request_val = http.formvalue(param_name)
+
+ if (type(required_val) == "string" and
+ request_val ~= required_val) or
+ (required_val == true and
+ (request_val == nil or request_val == ""))
+ then
+ return false
+ end
+ end
+
+ return true
+ end
+
+ return (target.post == true)
+ end
+
+ return false
+end
+
+function test_post_security()
+ if http.getenv("REQUEST_METHOD") ~= "POST" then
+ http.status(405, "Method Not Allowed")
+ http.header("Allow", "POST")
+ return false
+ end
+
+ if http.formvalue("token") ~= context.authtoken then
+ http.status(403, "Forbidden")
+ luci.template.render("csrftoken")
+ return false
+ end
+
+ return true
+end
+
function dispatch(request)
--context._disable_memtrace = require "luci.debug".trap_memtrace("l")
local ctx = context
@@ -206,7 +221,6 @@ function dispatch(request)
ctx.args = args
ctx.requestargs = ctx.requestargs or args
local n
- local token = ctx.urltoken
local preq = {}
local freq = {}
@@ -284,11 +298,14 @@ function dispatch(request)
resource = luci.config.main.resourcebase;
ifattr = function(...) return _ifattr(...) end;
attr = function(...) return _ifattr(true, ...) end;
+ url = build_url;
}, {__index=function(table, key)
if key == "controller" then
return build_url()
elseif key == "REQUEST_URI" then
return build_url(unpack(ctx.requestpath))
+ elseif key == "token" then
+ return ctx.authtoken
else
return rawget(table, key) or _G[key]
end
@@ -311,20 +328,17 @@ function dispatch(request)
local def = (type(track.sysauth) == "string") and track.sysauth
local accs = def and {track.sysauth} or track.sysauth
local sess = ctx.authsession
- local verifytoken = false
if not sess then
sess = http.getcookie("sysauth")
sess = sess and sess:match("^[a-f0-9]*$")
- verifytoken = true
end
local sdat = (util.ubus("session", "get", { ubus_rpc_session = sess }) or { }).values
- local user
+ local user, token
if sdat then
- if not verifytoken or ctx.urltoken.stok == sdat.token then
- user = sdat.user
- end
+ user = sdat.user
+ token = sdat.token
else
local eu = http.getenv("HTTP_AUTH_USER")
local ep = http.getenv("HTTP_AUTH_PASS")
@@ -357,12 +371,10 @@ function dispatch(request)
end
if sess and token then
- http.header("Set-Cookie", 'sysauth=%s; path=%s/' %{
- sess, build_url()
- })
+ http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sess, build_url() })
- ctx.urltoken.stok = token
ctx.authsession = sess
+ ctx.authtoken = token
ctx.authuser = user
http.redirect(build_url(unpack(ctx.requestpath)))
@@ -374,10 +386,17 @@ function dispatch(request)
end
else
ctx.authsession = sess
+ ctx.authtoken = token
ctx.authuser = user
end
end
+ if c and require_post_security(c.target) then
+ if not test_post_security(c) then
+ return
+ end
+ end
+
if track.setgroup then
sys.process.setgroup(track.setgroup)
end
@@ -703,6 +722,20 @@ function call(name, ...)
return {type = "call", argv = {...}, name = name, target = _call}
end
+function post_on(params, name, ...)
+ return {
+ type = "call",
+ post = params,
+ argv = { ... },
+ name = name,
+ target = _call
+ }
+end
+
+function post(...)
+ return post_on(true, ...)
+end
+
local _template = function(self, ...)
require "luci.template".render(self.view)
@@ -814,7 +847,13 @@ local function _cbi(self, ...)
end
function cbi(model, config)
- return {type = "cbi", config = config, model = model, target = _cbi}
+ return {
+ type = "cbi",
+ post = { ["cbi.submit"] = "1" },
+ config = config,
+ model = model,
+ target = _cbi
+ }
end
@@ -854,7 +893,12 @@ local function _form(self, ...)
end
function form(model)
- return {type = "cbi", model = model, target = _form}
+ return {
+ type = "cbi",
+ post = { ["cbi.submit"] = "1" },
+ model = model,
+ target = _form
+ }
end
translate = i18n.translate
diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua
index a92d8affb6..4b35731727 100644
--- a/modules/luci-base/luasrc/http.lua
+++ b/modules/luci-base/luasrc/http.lua
@@ -208,6 +208,7 @@ function splice(fd, size)
end
function redirect(url)
+ if url == "" then url = "/" end
status(302, "Found")
header("Location", url)
close()
diff --git a/modules/luci-base/luasrc/http/protocol.lua b/modules/luci-base/luasrc/http/protocol.lua
index 859272679f..0cb62aeec9 100644
--- a/modules/luci-base/luasrc/http/protocol.lua
+++ b/modules/luci-base/luasrc/http/protocol.lua
@@ -72,7 +72,7 @@ function urlencode( str )
if type(str) == "string" then
str = str:gsub(
- "([^a-zA-Z0-9$_%-%.!*'(),])",
+ "([^a-zA-Z0-9$_%-%.%~])",
__chrenc
)
end
diff --git a/modules/luci-base/luasrc/model/ipkg.lua b/modules/luci-base/luasrc/model/ipkg.lua
index 41633c43d7..e653b03465 100644
--- a/modules/luci-base/luasrc/model/ipkg.lua
+++ b/modules/luci-base/luasrc/model/ipkg.lua
@@ -127,26 +127,29 @@ local function _list(action, pat, cb)
(pat and (" '%s'" % pat:gsub("'", "")) or ""))
if fd then
- local name, version, desc
+ local name, version, sz, desc
while true do
local line = fd:read("*l")
if not line then break end
- name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
+ name, version, sz, desc = line:match("^(.-) %- (.-) %- (.-) %- (.+)")
if not name then
- name, version = line:match("^(.-) %- (.+)")
+ name, version, sz = line:match("^(.-) %- (.-) %- (.+)")
desc = ""
end
- if #version > 26 then
- version = version:sub(1,21) .. ".." .. version:sub(-3,-1)
- end
+ if name and version then
+ if #version > 26 then
+ version = version:sub(1,21) .. ".." .. version:sub(-3,-1)
+ end
- cb(name, version, desc)
+ cb(name, version, sz, desc)
+ end
name = nil
version = nil
+ sz = nil
desc = nil
end
@@ -155,15 +158,15 @@ local function _list(action, pat, cb)
end
function list_all(pat, cb)
- _list("list", pat, cb)
+ _list("list --size", pat, cb)
end
function list_installed(pat, cb)
- _list("list_installed", pat, cb)
+ _list("list_installed --size", pat, cb)
end
function find(pat, cb)
- _list("find", pat, cb)
+ _list("find --size", pat, cb)
end
@@ -237,4 +240,3 @@ function compare_versions(ver1, comp, ver2)
-- all equal and not compare greater or lower then true
return not (comp == "<" or comp == ">")
end
-
diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-base/luasrc/model/network.lua
index 20e1032760..81fc416fed 100644
--- a/modules/luci-base/luasrc/model/network.lua
+++ b/modules/luci-base/luasrc/model/network.lua
@@ -1,8 +1,8 @@
-- Copyright 2009-2015 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
-local type, next, pairs, ipairs, loadfile, table
- = type, next, pairs, ipairs, loadfile, table
+local type, next, pairs, ipairs, loadfile, table, select
+ = type, next, pairs, ipairs, loadfile, table, select
local tonumber, tostring, math = tonumber, tostring, math
@@ -16,6 +16,7 @@ local utl = require "luci.util"
local dsp = require "luci.dispatcher"
local uci = require "luci.model.uci"
local lng = require "luci.i18n"
+local jsc = require "luci.jsonc"
module "luci.model.network"
@@ -31,10 +32,10 @@ local _protocols = { }
local _interfaces, _bridge, _switch, _tunnel
local _ubusnetcache, _ubusdevcache, _ubuswificache
-local _uci_real, _uci_state
+local _uci
function _filter(c, s, o, r)
- local val = _uci_real:get(c, s, o)
+ local val = _uci:get(c, s, o)
if val then
local l = { }
if type(val) == "string" then
@@ -44,9 +45,9 @@ function _filter(c, s, o, r)
end
end
if #l > 0 then
- _uci_real:set(c, s, o, table.concat(l, " "))
+ _uci:set(c, s, o, table.concat(l, " "))
else
- _uci_real:delete(c, s, o)
+ _uci:delete(c, s, o)
end
elseif type(val) == "table" then
for _, val in ipairs(val) do
@@ -55,16 +56,16 @@ function _filter(c, s, o, r)
end
end
if #l > 0 then
- _uci_real:set(c, s, o, l)
+ _uci:set(c, s, o, l)
else
- _uci_real:delete(c, s, o)
+ _uci:delete(c, s, o)
end
end
end
end
function _append(c, s, o, a)
- local val = _uci_real:get(c, s, o) or ""
+ local val = _uci:get(c, s, o) or ""
if type(val) == "string" then
local l = { }
for val in val:gmatch("%S+") do
@@ -73,7 +74,7 @@ function _append(c, s, o, a)
end
end
l[#l+1] = a
- _uci_real:set(c, s, o, table.concat(l, " "))
+ _uci:set(c, s, o, table.concat(l, " "))
elseif type(val) == "table" then
local l = { }
for _, val in ipairs(val) do
@@ -82,7 +83,7 @@ function _append(c, s, o, a)
end
end
l[#l+1] = a
- _uci_real:set(c, s, o, l)
+ _uci:set(c, s, o, l)
end
end
@@ -95,15 +96,15 @@ function _stror(s1, s2)
end
function _get(c, s, o)
- return _uci_real:get(c, s, o)
+ return _uci:get(c, s, o)
end
function _set(c, s, o, v)
if v ~= nil then
if type(v) == "boolean" then v = v and "1" or "0" end
- return _uci_real:set(c, s, o, v)
+ return _uci:set(c, s, o, v)
else
- return _uci_real:delete(c, s, o)
+ return _uci:delete(c, s, o)
end
end
@@ -127,7 +128,7 @@ function _wifi_state(key, val, field)
for radio, radiostate in pairs(_ubuswificache) do
for ifc, ifcstate in pairs(radiostate.interfaces) do
if ifcstate.section and ifcstate.section:sub(1, 1) == '@' then
- local s = _uci_real:get_all('wireless.%s' % ifcstate.section)
+ local s = _uci:get_all('wireless.%s' % ifcstate.section)
if s then
ifcstate.section = s['.name']
end
@@ -153,7 +154,7 @@ function _wifi_lookup(ifn)
local num = 0
ifnidx = tonumber(ifnidx)
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device == radio then
num = num + 1
@@ -166,20 +167,9 @@ function _wifi_lookup(ifn)
return sid
- -- looks like wifi, try to locate the section via state vars
+ -- looks like wifi, try to locate the section via ubus state
elseif _wifi_iface(ifn) then
- local sid = _wifi_state("ifname", ifn, "section")
- if not sid then
- _uci_state:foreach("wireless", "wifi-iface",
- function(s)
- if s.ifname == ifn then
- sid = s['.name']
- return false
- end
- end)
- end
-
- return sid
+ return _wifi_state("ifname", ifn, "section")
end
end
@@ -205,8 +195,7 @@ end
function init(cursor)
- _uci_real = cursor or _uci_real or uci.cursor()
- _uci_state = _uci_real:substate()
+ _uci = cursor or _uci or uci.cursor()
_interfaces = { }
_bridge = { }
@@ -281,13 +270,13 @@ function init(cursor)
end
function save(self, ...)
- _uci_real:save(...)
- _uci_real:load(...)
+ _uci:save(...)
+ _uci:load(...)
end
function commit(self, ...)
- _uci_real:commit(...)
- _uci_real:load(...)
+ _uci:commit(...)
+ _uci:load(...)
end
function ifnameof(self, x)
@@ -345,7 +334,7 @@ end
function add_network(self, n, options)
local oldnet = self:get_network(n)
if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
- if _uci_real:section("network", "interface", n, options) then
+ if _uci:section("network", "interface", n, options) then
return network(n)
end
elseif oldnet and oldnet:is_empty() then
@@ -360,7 +349,7 @@ function add_network(self, n, options)
end
function get_network(self, n)
- if n and _uci_real:get("network", n) == "interface" then
+ if n and _uci:get("network", n) == "interface" then
return network(n)
end
end
@@ -369,7 +358,7 @@ function get_networks(self)
local nets = { }
local nls = { }
- _uci_real:foreach("network", "interface",
+ _uci:foreach("network", "interface",
function(s)
nls[s['.name']] = network(s['.name'])
end)
@@ -383,18 +372,18 @@ function get_networks(self)
end
function del_network(self, n)
- local r = _uci_real:delete("network", n)
+ local r = _uci:delete("network", n)
if r then
- _uci_real:delete_all("network", "alias",
+ _uci:delete_all("network", "alias",
function(s) return (s.interface == n) end)
- _uci_real:delete_all("network", "route",
+ _uci:delete_all("network", "route",
function(s) return (s.interface == n) end)
- _uci_real:delete_all("network", "route6",
+ _uci:delete_all("network", "route6",
function(s) return (s.interface == n) end)
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
local net
local rest = { }
@@ -404,10 +393,10 @@ function del_network(self, n)
end
end
if #rest > 0 then
- _uci_real:set("wireless", s['.name'], "network",
+ _uci:set("wireless", s['.name'], "network",
table.concat(rest, " "))
else
- _uci_real:delete("wireless", s['.name'], "network")
+ _uci:delete("wireless", s['.name'], "network")
end
end)
end
@@ -417,31 +406,31 @@ end
function rename_network(self, old, new)
local r
if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
- r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
+ r = _uci:section("network", "interface", new, _uci:get_all("network", old))
if r then
- _uci_real:foreach("network", "alias",
+ _uci:foreach("network", "alias",
function(s)
if s.interface == old then
- _uci_real:set("network", s['.name'], "interface", new)
+ _uci:set("network", s['.name'], "interface", new)
end
end)
- _uci_real:foreach("network", "route",
+ _uci:foreach("network", "route",
function(s)
if s.interface == old then
- _uci_real:set("network", s['.name'], "interface", new)
+ _uci:set("network", s['.name'], "interface", new)
end
end)
- _uci_real:foreach("network", "route6",
+ _uci:foreach("network", "route6",
function(s)
if s.interface == old then
- _uci_real:set("network", s['.name'], "interface", new)
+ _uci:set("network", s['.name'], "interface", new)
end
end)
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
local net
local list = { }
@@ -453,12 +442,12 @@ function rename_network(self, old, new)
end
end
if #list > 0 then
- _uci_real:set("wireless", s['.name'], "network",
+ _uci:set("wireless", s['.name'], "network",
table.concat(list, " "))
end
end)
- _uci_real:delete("network", old)
+ _uci:delete("network", old)
end
end
return r or false
@@ -470,7 +459,7 @@ function get_interface(self, i)
else
local ifc
local num = { }
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device then
num[s.device] = num[s.device] and num[s.device] + 1 or 1
@@ -485,6 +474,21 @@ function get_interface(self, i)
end
end
+local function swdev_from_board_json()
+ local boardinfo = jsc.parse(nfs.readfile("/etc/board.json") or "")
+ if type(boardinfo) == "table" and type(boardinfo.network) == "table" then
+ local net, val
+ for net, val in pairs(boardinfo.network) do
+ if type(val) == "table" and type(val.ifname) == "string" and
+ val.create_vlan == true
+ then
+ return val.ifname
+ end
+ end
+ end
+ return nil
+end
+
function get_interfaces(self)
local iface
local ifaces = { }
@@ -493,7 +497,7 @@ function get_interfaces(self)
local baseof = { }
-- find normal interfaces
- _uci_real:foreach("network", "interface",
+ _uci:foreach("network", "interface",
function(s)
for iface in utl.imatch(s.ifname) do
if not _iface_ignore(iface) and not _wifi_iface(iface) then
@@ -510,7 +514,7 @@ function get_interfaces(self)
end
-- find vlan interfaces
- _uci_real:foreach("network", "switch_vlan",
+ _uci:foreach("network", "switch_vlan",
function(s)
if not s.device then
return
@@ -526,7 +530,7 @@ function get_interfaces(self)
end
end
if not base or not base:match("^eth%d") then
- base = "eth0"
+ base = swdev_from_board_json() or "eth0"
end
else
base = s.device
@@ -551,7 +555,7 @@ function get_interfaces(self)
-- find wifi interfaces
local num = { }
local wfs = { }
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device then
num[s.device] = num[s.device] and num[s.device] + 1 or 1
@@ -572,7 +576,7 @@ function ignore_interface(self, x)
end
function get_wifidev(self, dev)
- if _uci_real:get("wireless", dev) == "wifi-device" then
+ if _uci:get("wireless", dev) == "wifi-device" then
return wifidev(dev)
end
end
@@ -581,7 +585,7 @@ function get_wifidevs(self)
local devs = { }
local wfd = { }
- _uci_real:foreach("wireless", "wifi-device",
+ _uci:foreach("wireless", "wifi-device",
function(s) wfd[#wfd+1] = s['.name'] end)
local dev
@@ -601,9 +605,9 @@ end
function add_wifinet(self, net, options)
if type(options) == "table" and options.device and
- _uci_real:get("wireless", options.device) == "wifi-device"
+ _uci:get("wireless", options.device) == "wifi-device"
then
- local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
+ local wnet = _uci:section("wireless", "wifi-iface", nil, options)
return wifinet(wnet)
end
end
@@ -611,7 +615,7 @@ end
function del_wifinet(self, net)
local wnet = _wifi_lookup(net)
if wnet then
- _uci_real:delete("wireless", wnet)
+ _uci:delete("wireless", wnet)
return true
end
return false
@@ -684,7 +688,7 @@ end
function network(name, proto)
if name then
- local p = proto or _uci_real:get("network", name, "proto")
+ local p = proto or _uci:get("network", name, "proto")
local c = p and _protocols[p] or protocol
return c(name)
end
@@ -695,7 +699,7 @@ function protocol.__init__(self, name)
end
function protocol._get(self, opt)
- local v = _uci_real:get("network", self.sid, opt)
+ local v = _uci:get("network", self.sid, opt)
if type(v) == "table" then
return table.concat(v, " ")
end
@@ -730,7 +734,7 @@ function protocol.ifname(self)
end
if not ifname then
local num = { }
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device then
num[s.device] = num[s.device]
@@ -779,17 +783,21 @@ function protocol.uptime(self)
end
function protocol.expires(self)
- local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
- local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
- if a and l then
- l = l - (nxo.sysinfo().uptime - a)
- return l > 0 and l or 0
+ local u = self:_ubus("uptime")
+ local d = self:_ubus("data")
+
+ if type(u) == "number" and type(d) == "table" and
+ type(d.leasetime) == "number"
+ then
+ local r = (d.leasetime - (u % d.leasetime))
+ return r > 0 and r or 0
end
+
return -1
end
function protocol.metric(self)
- return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
+ return self:_ubus("metric") or 0
end
function protocol.ipaddr(self)
@@ -797,6 +805,20 @@ function protocol.ipaddr(self)
return addrs and #addrs > 0 and addrs[1].address
end
+function protocol.ipaddrs(self)
+ local addrs = self:_ubus("ipv4-address")
+ local rv = { }
+
+ if type(addrs) == "table" then
+ local n, addr
+ for n, addr in ipairs(addrs) do
+ rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
+ end
+ end
+
+ return rv
+end
+
function protocol.netmask(self)
local addrs = self:_ubus("ipv4-address")
return addrs and #addrs > 0 and
@@ -835,6 +857,28 @@ function protocol.ip6addr(self)
end
end
+function protocol.ip6addrs(self)
+ local addrs = self:_ubus("ipv6-address")
+ local rv = { }
+ local n, addr
+
+ if type(addrs) == "table" then
+ for n, addr in ipairs(addrs) do
+ rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
+ end
+ end
+
+ addrs = self:_ubus("ipv6-prefix-assignment")
+
+ if type(addrs) == "table" then
+ for n, addr in ipairs(addrs) do
+ rv[#rv+1] = "%s1/%d" %{ addr.address, addr.mask }
+ end
+ end
+
+ return rv
+end
+
function protocol.gw6addr(self)
local _, route
for _, route in ipairs(self:_ubus("route") or { }) do
@@ -885,7 +929,7 @@ function protocol.is_empty(self)
rv = false
end
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
local n
for n in utl.imatch(s.network) do
@@ -937,12 +981,12 @@ function protocol.get_interface(self)
else
local ifn = nil
local num = { }
- for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
+ for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
ifn = ifn:match("^[^:/]+")
return ifn and interface(ifn, self)
end
ifn = nil
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device then
num[s.device] = num[s.device] and num[s.device] + 1 or 1
@@ -977,7 +1021,7 @@ function protocol.get_interfaces(self)
local num = { }
local wfs = { }
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device then
num[s.device] = num[s.device] and num[s.device] + 1 or 1
@@ -1020,7 +1064,7 @@ function protocol.contains_interface(self, ifname)
local wif = _wifi_lookup(ifname)
if wif then
local n
- for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
+ for n in utl.imatch(_uci:get("wireless", wif, "network")) do
if n == self.sid then
return true
end
@@ -1066,7 +1110,8 @@ function interface.name(self)
end
function interface.mac(self)
- return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
+ local mac = self:_ubus("macaddr")
+ return mac and mac:upper()
end
function interface.ipaddrs(self)
@@ -1278,22 +1323,11 @@ function wifidev.is_up(self)
return (_ubuswificache[self.sid].up == true)
end
- local up = false
- _uci_state:foreach("wireless", "wifi-iface",
- function(s)
- if s.device == self.sid then
- if s.up == "1" then
- up = true
- return false
- end
- end
- end)
-
- return up
+ return false
end
function wifidev.get_wifinet(self, net)
- if _uci_real:get("wireless", net) == "wifi-iface" then
+ if _uci:get("wireless", net) == "wifi-iface" then
return wifinet(net)
else
local wnet = _wifi_lookup(net)
@@ -1306,7 +1340,7 @@ end
function wifidev.get_wifinets(self)
local nets = { }
- _uci_real:foreach("wireless", "wifi-iface",
+ _uci:foreach("wireless", "wifi-iface",
function(s)
if s.device == self.sid then
nets[#nets+1] = wifinet(s['.name'])
@@ -1320,7 +1354,7 @@ function wifidev.add_wifinet(self, options)
options = options or { }
options.device = self.sid
- local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
+ local wnet = _uci:section("wireless", "wifi-iface", nil, options)
if wnet then
return wifinet(wnet, options)
end
@@ -1329,12 +1363,12 @@ end
function wifidev.del_wifinet(self, net)
if utl.instanceof(net, wifinet) then
net = net.sid
- elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
+ elseif _uci:get("wireless", net) ~= "wifi-iface" then
net = _wifi_lookup(net)
end
- if net and _uci_real:get("wireless", net, "device") == self.sid then
- _uci_real:delete("wireless", net)
+ if net and _uci:get("wireless", net, "device") == self.sid then
+ _uci:delete("wireless", net)
return true
end
@@ -1347,26 +1381,58 @@ wifinet = utl.class()
function wifinet.__init__(self, net, data)
self.sid = net
+ local n = 0
local num = { }
- local netid
- _uci_real:foreach("wireless", "wifi-iface",
+ local netid, sid
+ _uci:foreach("wireless", "wifi-iface",
function(s)
+ n = n + 1
if s.device then
num[s.device] = num[s.device] and num[s.device] + 1 or 1
if s['.name'] == self.sid then
+ sid = "@wifi-iface[%d]" % n
netid = "%s.network%d" %{ s.device, num[s.device] }
return false
end
end
end)
+ if sid then
+ local _, k, r, i
+ for k, r in pairs(_ubuswificache) do
+ if type(r) == "table" and
+ type(r.interfaces) == "table"
+ then
+ for _, i in ipairs(r.interfaces) do
+ if type(i) == "table" and i.section == sid then
+ self._ubusdata = {
+ radio = k,
+ dev = r,
+ net = i
+ }
+ end
+ end
+ end
+ end
+ end
+
local dev = _wifi_state("section", self.sid, "ifname") or netid
self.netid = netid
self.wdev = dev
self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
- self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
- _uci_real:get_all("wireless", self.sid) or { }
+end
+
+function wifinet.ubus(self, ...)
+ local n, v = self._ubusdata
+ for n = 1, select('#', ...) do
+ if type(v) == "table" then
+ v = v[select(n, ...)]
+ else
+ return nil
+ end
+ end
+ return v
end
function wifinet.get(self, opt)
@@ -1378,19 +1444,23 @@ function wifinet.set(self, opt, val)
end
function wifinet.mode(self)
- return _uci_state:get("wireless", self.sid, "mode") or "ap"
+ return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
end
function wifinet.ssid(self)
- return _uci_state:get("wireless", self.sid, "ssid")
+ return self:ubus("net", "config", "ssid") or self:get("ssid")
end
function wifinet.bssid(self)
- return _uci_state:get("wireless", self.sid, "bssid")
+ return self:ubus("net", "config", "bssid") or self:get("bssid")
end
function wifinet.network(self)
- return _uci_state:get("wifinet", self.sid, "network")
+ local net, networks = nil, { }
+ for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
+ networks[#networks+1] = net
+ end
+ return networks
end
function wifinet.id(self)
@@ -1402,7 +1472,7 @@ function wifinet.name(self)
end
function wifinet.ifname(self)
- local ifname = self.iwinfo.ifname
+ local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
ifname = self.wdev
end
@@ -1410,9 +1480,8 @@ function wifinet.ifname(self)
end
function wifinet.get_device(self)
- if self.iwdata.device then
- return wifidev(self.iwdata.device)
- end
+ local dev = self:ubus("radio") or self:get("device")
+ return dev and wifidev(dev) or nil
end
function wifinet.is_up(self)
@@ -1421,7 +1490,7 @@ function wifinet.is_up(self)
end
function wifinet.active_mode(self)
- local m = _stror(self.iwdata.mode, self.iwinfo.mode) or "ap"
+ local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
if m == "ap" then m = "Master"
elseif m == "sta" then m = "Client"
@@ -1438,11 +1507,11 @@ function wifinet.active_mode_i18n(self)
end
function wifinet.active_ssid(self)
- return _stror(self.iwdata.ssid, self.iwinfo.ssid)
+ return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
end
function wifinet.active_bssid(self)
- return _stror(self.iwdata.bssid, self.iwinfo.bssid) or "00:00:00:00:00:00"
+ return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
end
function wifinet.active_encryption(self)
@@ -1469,8 +1538,8 @@ function wifinet.bitrate(self)
end
function wifinet.channel(self)
- return self.iwinfo.channel or
- tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
+ return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
+ tonumber(self:get("channel"))
end
function wifinet.signal(self)
@@ -1482,7 +1551,7 @@ function wifinet.noise(self)
end
function wifinet.country(self)
- return self.iwinfo.country or "00"
+ return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
end
function wifinet.txpower(self)
@@ -1548,8 +1617,8 @@ end
function wifinet.get_networks(self)
local nets = { }
local net
- for net in utl.imatch(tostring(self.iwdata.network)) do
- if _uci_real:get("network", net) == "interface" then
+ for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
+ if _uci:get("network", net) == "interface" then
nets[#nets+1] = network(net)
end
end
diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc
index 9e4a58f1d9..49093c7930 100644
--- a/modules/luci-base/luasrc/model/uci.luadoc
+++ b/modules/luci-base/luasrc/model/uci.luadoc
@@ -237,12 +237,18 @@ Saves changes made to a config to make them committable.
---[[
Set a value or create a named section.
+When invoked with three arguments `config`, `sectionname`, `sectiontype`,
+then a named section of the given type is created.
+
+When invoked with four arguments `config`, `sectionname`, `optionname` and
+`optionvalue` then the value of the specified option is set to the given value.
+
@class function
@name Cursor.set
@param config UCI config
@param section UCI section name
@param option UCI option or UCI section type
-@param value UCI value or nil if you want to create a section
+@param value UCI value or nothing if you want to create a section
@return Boolean whether operation succeeded
]]
diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua
index 3977da3eda..31c3e12095 100644
--- a/modules/luci-base/luasrc/sys.lua
+++ b/modules/luci-base/luasrc/sys.lua
@@ -293,47 +293,51 @@ function net.ipv6_hints(callback)
end
function net.conntrack(callback)
- local connt = {}
- if fs.access("/proc/net/nf_conntrack", "r") then
- for line in io.lines("/proc/net/nf_conntrack") do
- line = line:match "^(.-( [^ =]+=).-)%2"
- local entry, flags = _parse_mixed_record(line, " +")
- if flags[6] ~= "TIME_WAIT" then
- entry.layer3 = flags[1]
- entry.layer4 = flags[3]
- for i=1, #entry do
- entry[i] = nil
- end
+ local ok, nfct = pcall(io.lines, "/proc/net/nf_conntrack")
+ if not ok or not nfct then
+ return nil
+ end
- if callback then
- callback(entry)
- else
- connt[#connt+1] = entry
+ local line, connt = nil, (not callback) and { }
+ for line in nfct do
+ local fam, l3, l4, timeout, state, tuples =
+ line:match("^(ipv[46]) +(%d+) +%S+ +(%d+) +(%d+) +([A-Z_]+) +(.+)$")
+
+ if fam and l3 and l4 and timeout and state and tuples and
+ state ~= "TIME_WAIT"
+ then
+ l4 = nixio.getprotobynumber(l4)
+
+ local entry = {
+ bytes = 0,
+ packets = 0,
+ layer3 = fam,
+ layer4 = l4 and l4.name or "unknown",
+ timeout = tonumber(timeout, 10)
+ }
+
+ local key, val
+ for key, val in tuples:gmatch("(%w+)=(%S+)") do
+ if key == "bytes" or key == "packets" then
+ entry[key] = entry[key] + tonumber(val, 10)
+ elseif key == "src" or key == "dst" or key == "sport" or key == "dport" then
+ if entry[key] == nil then
+ entry[key] = val
+ end
+ elseif val then
+ entry[key] = val
end
end
- end
- elseif fs.access("/proc/net/ip_conntrack", "r") then
- for line in io.lines("/proc/net/ip_conntrack") do
- line = line:match "^(.-( [^ =]+=).-)%2"
- local entry, flags = _parse_mixed_record(line, " +")
- if flags[4] ~= "TIME_WAIT" then
- entry.layer3 = "ipv4"
- entry.layer4 = flags[1]
- for i=1, #entry do
- entry[i] = nil
- end
- if callback then
- callback(entry)
- else
- connt[#connt+1] = entry
- end
+ if callback then
+ callback(entry)
+ else
+ connt[#connt+1] = entry
end
end
- else
- return nil
end
- return connt
+
+ return callback and true or connt
end
function net.devices()
@@ -581,30 +585,20 @@ function wifi.getiwinfo(ifname)
local stat, iwinfo = pcall(require, "iwinfo")
if ifname then
- local c = 0
- local u = uci.cursor_state()
local d, n = ifname:match("^(%w+)%.network(%d+)")
- if d and n then
+ local wstate = luci.util.ubus("network.wireless", "status") or { }
+
+ d = d or ifname
+ n = n and tonumber(n) or 1
+
+ if type(wstate[d]) == "table" and
+ type(wstate[d].interfaces) == "table" and
+ type(wstate[d].interfaces[n]) == "table" and
+ type(wstate[d].interfaces[n].ifname) == "string"
+ then
+ ifname = wstate[d].interfaces[n].ifname
+ else
ifname = d
- n = tonumber(n)
- u:foreach("wireless", "wifi-iface",
- function(s)
- if s.device == d then
- c = c + 1
- if c == n then
- ifname = s.ifname or s.device
- return false
- end
- end
- end)
- elseif u:get("wireless", ifname) == "wifi-device" then
- u:foreach("wireless", "wifi-iface",
- function(s)
- if s.device == ifname and s.ifname then
- ifname = s.ifname
- return false
- end
- end)
end
local t = stat and iwinfo.type(ifname)
@@ -665,28 +659,3 @@ end
function init.stop(name)
return (init_action("stop", name) == 0)
end
-
-
--- Internal functions
-
-function _parse_mixed_record(cnt, delimiter)
- delimiter = delimiter or " "
- local data = {}
- local flags = {}
-
- for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
- for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
- local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
-
- if k then
- if x == "" then
- table.insert(flags, k)
- else
- data[k] = v
- end
- end
- end
- end
-
- return data, flags
-end
diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua
index b109c712ec..ff13b1ccd2 100644
--- a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua
+++ b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua
@@ -105,6 +105,7 @@ TZ = {
{ 'America/Edmonton', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Eirunepe', 'ACT5' },
{ 'America/El Salvador', 'CST6' },
+ { 'America/Fort Nelson', 'MST7' },
{ 'America/Fortaleza', 'BRT3' },
{ 'America/Glace Bay', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'America/Godthab', 'WGT3WGST,M3.5.0/-2,M10.5.0/-1' },
@@ -388,7 +389,7 @@ TZ = {
{ 'Pacific/Efate', 'VUT-11' },
{ 'Pacific/Enderbury', 'PHOT-13' },
{ 'Pacific/Fakaofo', 'TKT-13' },
- { 'Pacific/Fiji', 'FJT-12FJST,M11.1.0,M1.3.4/75' },
+ { 'Pacific/Fiji', 'FJT-12FJST,M11.1.0,M1.3.0/3' },
{ 'Pacific/Funafuti', 'TVT-12' },
{ 'Pacific/Galapagos', 'GALT6' },
{ 'Pacific/Gambier', 'GAMT9' },
@@ -404,7 +405,7 @@ TZ = {
{ 'Pacific/Midway', 'SST11' },
{ 'Pacific/Nauru', 'NRT-12' },
{ 'Pacific/Niue', 'NUT11' },
- { 'Pacific/Norfolk', 'NFT-11:30' },
+ { 'Pacific/Norfolk', 'NFT-11' },
{ 'Pacific/Noumea', 'NCT-11' },
{ 'Pacific/Pago Pago', 'SST11' },
{ 'Pacific/Palau', 'PWT-9' },
diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua
index f156f3663a..53c8adbc69 100644
--- a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua
+++ b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua
@@ -152,7 +152,7 @@ OFFSET = {
sst = -39600, -- SST
nrt = 43200, -- NRT
nut = -39600, -- NUT
- nft = 41400, -- NFT
+ nft = 39600, -- NFT
nct = 39600, -- NCT
pwt = 32400, -- PWT
pont = 39600, -- PONT
diff --git a/modules/luci-base/luasrc/util.lua b/modules/luci-base/luasrc/util.lua
index dcf8230b3e..5bf0beb6c2 100644
--- a/modules/luci-base/luasrc/util.lua
+++ b/modules/luci-base/luasrc/util.lua
@@ -9,6 +9,7 @@ local ldebug = require "luci.debug"
local string = require "string"
local coroutine = require "coroutine"
local tparser = require "luci.template.parser"
+local json = require "luci.jsonc"
local _ubus = require "ubus"
local _ubus_connection = nil
@@ -150,6 +151,28 @@ function striptags(value)
return value and tparser.striptags(tostring(value))
end
+-- for bash, ash and similar shells single-quoted strings are taken
+-- literally except for single quotes (which terminate the string)
+-- (and the exception noted below for dash (-) at the start of a
+-- command line parameter).
+function shellsqescape(value)
+ local res
+ res, _ = string.gsub(res, "'", "'\\''")
+ return res
+end
+
+-- bash, ash and other similar shells interpret a dash (-) at the start
+-- of a command-line parameters as an option indicator regardless of
+-- whether it is inside a single-quoted string. It must be backlash
+-- escaped to resolve this. This requires in some funky special-case
+-- handling. It may actually be a property of the getopt function
+-- rather than the shell proper.
+function shellstartsqescape(value)
+ res, _ = string.gsub(value, "^\-", "\\-")
+ res, _ = string.gsub(res, "^-", "\-")
+ return shellsqescape(value)
+end
+
-- containing the resulting substrings. The optional max parameter specifies
-- the number of bytes to process, regardless of the actual length of the given
-- string. The optional last parameter, regex, specifies whether the separator
@@ -600,55 +623,11 @@ function ubus(object, method, data)
end
function serialize_json(x, cb)
- local rv, push = nil, cb
- if not push then
- rv = { }
- push = function(tok) rv[#rv+1] = tok end
- end
-
- if x == nil then
- push("null")
- elseif type(x) == "table" then
- -- test if table is array like
- local k, v
- local n1, n2 = 0, 0
- for k in pairs(x) do n1 = n1 + 1 end
- for k in ipairs(x) do n2 = n2 + 1 end
-
- if n1 == n2 and n1 > 0 then
- push("[")
- for k = 1, n2 do
- if k > 1 then
- push(",")
- end
- serialize_json(x[k], push)
- end
- push("]")
- else
- push("{")
- for k, v in pairs(x) do
- push("%q:" % tostring(k))
- serialize_json(v, push)
- if next(x, k) then
- push(",")
- end
- end
- push("}")
- end
- elseif type(x) == "number" or type(x) == "boolean" then
- if (x ~= x) then
- -- NaN is the only value that doesn't equal to itself.
- push("Number.NaN")
- else
- push(tostring(x))
- end
+ local js = json.stringify(x)
+ if type(cb) == "function" then
+ cb(js)
else
- push('"%s"' % tostring(x):gsub('["%z\1-\31\\]',
- function(c) return '\\u%04x' % c:byte(1) end))
- end
-
- if not cb then
- return table.concat(rv, "")
+ return js
end
end
diff --git a/modules/luci-base/luasrc/view/cbi/apply_xhr.htm b/modules/luci-base/luasrc/view/cbi/apply_xhr.htm
index 1814c9393b..daa57c1db7 100644
--- a/modules/luci-base/luasrc/view/cbi/apply_xhr.htm
+++ b/modules/luci-base/luasrc/view/cbi/apply_xhr.htm
@@ -4,10 +4,10 @@
<script type="text/javascript">//<![CDATA[
var apply_xhr = new XHR();
- apply_xhr.get('<%=luci.dispatcher.build_url("servicectl", "restart", table.concat(configs, ","))%>', null,
+ apply_xhr.post('<%=url('servicectl/restart', table.concat(configs, ","))%>', { token: '<%=token%>' },
function() {
var checkfinish = function() {
- apply_xhr.get('<%=luci.dispatcher.build_url("servicectl", "status")%>', null,
+ apply_xhr.get('<%=url('servicectl/status')%>', null,
function(x) {
if( x.responseText == 'finish' )
{
diff --git a/modules/luci-base/luasrc/view/cbi/browser.htm b/modules/luci-base/luasrc/view/cbi/browser.htm
index e4a4077d55..a18120141d 100644
--- a/modules/luci-base/luasrc/view/cbi/browser.htm
+++ b/modules/luci-base/luasrc/view/cbi/browser.htm
@@ -2,6 +2,6 @@
<%+cbi/valueheader%>
<input class="cbi-input-text" type="text"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
<script type="text/javascript">
-cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=luci.dispatcher.build_url("admin", "filebrowser")%>'<%=self.default_path and ", '"..self.default_path.."'"%>);
+cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=url('admin/filebrowser')%>'<%=self.default_path and ", '"..self.default_path.."'"%>);
</script>
<%+cbi/valuefooter%>
diff --git a/modules/luci-base/luasrc/view/cbi/dynlist.htm b/modules/luci-base/luasrc/view/cbi/dynlist.htm
index fd626a4ecf..e936c0c39f 100644
--- a/modules/luci-base/luasrc/view/cbi/dynlist.htm
+++ b/modules/luci-base/luasrc/view/cbi/dynlist.htm
@@ -15,7 +15,9 @@
<script type="text/javascript">
cbi_dynlist_init(
'<%=cbid%>', '<%=resource%>', '<%=self.datatype%>',
- <%=tostring(self.optional or self.rmempty)%>
+ <%=tostring(self.optional or self.rmempty)%>,
+ '<%=url('admin/filebrowser')%>',
+ '<%=self.default_path and self.default_path%>'
<%- if #self.keylist > 0 then -%>, [{
<%- for i, k in ipairs(self.keylist) do -%>
<%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
diff --git a/modules/luci-base/luasrc/view/cbi/error.htm b/modules/luci-base/luasrc/view/cbi/error.htm
index 2acb96924e..75ec1082aa 100644
--- a/modules/luci-base/luasrc/view/cbi/error.htm
+++ b/modules/luci-base/luasrc/view/cbi/error.htm
@@ -1,5 +1,5 @@
<div class="cbi-map" id="cbi-<%=self.config%>">
- <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
+ <% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
<p class="alert-message danger">
diff --git a/modules/luci-base/luasrc/view/cbi/header.htm b/modules/luci-base/luasrc/view/cbi/header.htm
index 2bddaba61a..302df1d2fd 100644
--- a/modules/luci-base/luasrc/view/cbi/header.htm
+++ b/modules/luci-base/luasrc/view/cbi/header.htm
@@ -2,6 +2,7 @@
<form method="post" name="cbi" action="<%=REQUEST_URI%>" enctype="multipart/form-data" onreset="return cbi_validate_reset(this)" onsubmit="return cbi_validate_form(this, '<%:Some fields are invalid, cannot save values!%>')">
<div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+ <input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="cbi.submit" value="1" />
<input type="submit" value="<%:Save%>" class="hidden" />
</div>
diff --git a/modules/luci-base/luasrc/view/cbi/map.htm b/modules/luci-base/luasrc/view/cbi/map.htm
index 053220d185..e90c3f589f 100644
--- a/modules/luci-base/luasrc/view/cbi/map.htm
+++ b/modules/luci-base/luasrc/view/cbi/map.htm
@@ -5,7 +5,7 @@
<%-+cbi/apply_xhr-%>
<div class="cbi-map" id="cbi-<%=self.config%>">
- <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
+ <% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
<%- if firstmap and applymap then cbi_apply_xhr(self.config, parsechain, redirect) end -%>
<%- self:render_children() %>
diff --git a/modules/luci-base/luasrc/view/cbi/mvalue.htm b/modules/luci-base/luasrc/view/cbi/mvalue.htm
index 6a0b3881d0..5d092610ed 100644
--- a/modules/luci-base/luasrc/view/cbi/mvalue.htm
+++ b/modules/luci-base/luasrc/view/cbi/mvalue.htm
@@ -12,7 +12,7 @@
c = c + 1
%>
<input class="cbi-input-checkbox" type="checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr(luci.util.contains(v, key), "checked", "checked") %> />
- <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label><br />
+ <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label><% if not self.oneline then %><br /><% else %> <% end %>
<% if c == self.size then c = 0 %><br />
<% end end %>
<% end %>
diff --git a/modules/luci-base/luasrc/view/cbi/simpleform.htm b/modules/luci-base/luasrc/view/cbi/simpleform.htm
index 5216cd50f1..78f5c5a544 100644
--- a/modules/luci-base/luasrc/view/cbi/simpleform.htm
+++ b/modules/luci-base/luasrc/view/cbi/simpleform.htm
@@ -2,11 +2,12 @@
<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>">
<div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+ <input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="cbi.submit" value="1" />
</div>
<% end %>
<div class="cbi-map" id="cbi-<%=self.config%>">
- <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
+ <% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
<% self:render_children() %>
<br />
diff --git a/modules/luci-base/luasrc/view/cbi/upload.htm b/modules/luci-base/luasrc/view/cbi/upload.htm
index 7770934111..157f3b36fa 100644
--- a/modules/luci-base/luasrc/view/cbi/upload.htm
+++ b/modules/luci-base/luasrc/view/cbi/upload.htm
@@ -6,9 +6,19 @@
<%+cbi/valueheader%>
<% if s then %>
<%:Uploaded File%> (<%=t.byte_format(s.size)%>)
- <input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
- <input class="cbi-button cbi-input-image" type="image" value="<%:Replace entry%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" />
- <% else %>
+ <% if self.unsafeupload then %>
+ <input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
+ <input class="cbi-button cbi-input-image" type="image" value="<%:Replace entry%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" />
+ <% end %>
+ <% end %>
+
+ <% if not self.unsafeupload then %>
+ <input type="hidden"<%= attr("value", v) .. attr("name", "cbi.rlf." .. section .. "." .. self.option) .. attr("id", "cbi.rlf." .. section .. "." .. self.option) %> />
+ <% end %>
+
+ <% if (not s) or (s and not self.unsafeupload) then %>
<input class="cbi-input-file" type="file"<%= attr("name", cbid) .. attr("id", cbid) %> />
<% end %>
+ <input type="text" class="cbi-input-text" onchange="cbi_d_update(this.id)"<%=
+ attr("name", cbid .. ".textbox") .. attr("id", cbid .. ".textbox") .. attr("value", luci.cbi.AbstractValue.cfgvalue(self, section) or self.default) .. ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") .. ifattr(self.readonly, "readonly") .. ifattr(self.maxlength, "maxlength") %> />
<%+cbi/valuefooter%>
diff --git a/modules/luci-base/luasrc/view/cbi/value.htm b/modules/luci-base/luasrc/view/cbi/value.htm
index d1a7bea5c6..c43dab5f4b 100644
--- a/modules/luci-base/luasrc/view/cbi/value.htm
+++ b/modules/luci-base/luasrc/view/cbi/value.htm
@@ -1,7 +1,8 @@
<%+cbi/valueheader%>
<input type="<%=self.password and 'password" class="cbi-input-password' or 'text" class="cbi-input-text' %>" onchange="cbi_d_update(this.id)"<%=
attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) ..
- ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder")
+ ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") ..
+ ifattr(self.readonly, "readonly") .. ifattr(self.maxlength, "maxlength")
%> />
<% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
<% if #self.keylist > 0 or self.datatype then -%>
@@ -28,7 +29,7 @@
<%- end -%>');
<%- end %>
<% if self.datatype then -%>
- cbi_validate_field('<%=cbid%>', <%=tostring((self.optional or self.rmempty) == true)%>, '<%=self.datatype:gsub("'", "\\'")%>');
+ cbi_validate_field('<%=cbid%>', <%=tostring((self.optional or self.rmempty) == true)%>, '<%=self.datatype:gsub("\\", "\\\\"):gsub("'", "\\'")%>');
<%- end %>
//]]></script>
<% end -%>
diff --git a/modules/luci-base/luasrc/view/csrftoken.htm b/modules/luci-base/luasrc/view/csrftoken.htm
new file mode 100644
index 0000000000..57ac03f3bf
--- /dev/null
+++ b/modules/luci-base/luasrc/view/csrftoken.htm
@@ -0,0 +1,24 @@
+<%#
+ Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<h2 name="content"><%:Form token mismatch%></h2>
+<br />
+
+<p class="alert-message"><%:The submitted security token is invalid or already expired!%></p>
+
+<p><%:
+ In order to prevent unauthorized access to the system, your request has
+ been blocked. Click "Continue »" below to return to the previous page.
+%></p>
+
+<hr />
+
+<p class="right">
+ <strong><a href="#" onclick="window.history.back();">Continue »</a></strong>
+</p>
+
+<%+footer%>
diff --git a/modules/luci-base/luasrc/view/error404.htm b/modules/luci-base/luasrc/view/error404.htm
index c2be29ed58..bc74226830 100644
--- a/modules/luci-base/luasrc/view/error404.htm
+++ b/modules/luci-base/luasrc/view/error404.htm
@@ -5,7 +5,7 @@
-%>
<%+header%>
-<h2><a id="content" name="content">404 <%:Not Found%></a></h2>
+<h2 name="content">404 <%:Not Found%></h2>
<p><%:Sorry, the object you requested was not found.%></p>
<tt><%:Unable to dispatch%>: <%=luci.http.request.env.PATH_INFO%></tt>
<%+footer%>
diff --git a/modules/luci-base/luasrc/view/error500.htm b/modules/luci-base/luasrc/view/error500.htm
index 8fb18ed076..34a52cda84 100644
--- a/modules/luci-base/luasrc/view/error500.htm
+++ b/modules/luci-base/luasrc/view/error500.htm
@@ -5,7 +5,7 @@
-%>
<%+header%>
-<h2><a id="content" name="content">500 <%:Internal Server Error%></a></h2>
+<h2 name="content">500 <%:Internal Server Error%></h2>
<p><%:Sorry, the server encountered an unexpected error.%></p>
<pre class="error500"><%=message%></pre>
<%+footer%>
diff --git a/modules/luci-base/luasrc/view/sysauth.htm b/modules/luci-base/luasrc/view/sysauth.htm
index 7f0f0a622b..e207504911 100644
--- a/modules/luci-base/luasrc/view/sysauth.htm
+++ b/modules/luci-base/luasrc/view/sysauth.htm
@@ -8,7 +8,7 @@
<form method="post" action="<%=pcdata(luci.http.getenv("REQUEST_URI"))%>">
<div class="cbi-map">
- <h2><a id="content" name="content"><%:Authorization Required%></a></h2>
+ <h2 name="content"><%:Authorization Required%></h2>
<div class="cbi-map-descr">
<%:Please enter your username and password.%>
<%- if fuser then %>